|
|
12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,看看你能做出几个!; {5 |+ o4 d3 t* }7 d
1.gets函数
/ k: G; p& ^( j1 ]" H3 V2 Y _! h, o7 N. s! T3 D w$ ^
问:请找出下面代码里的问题:
5 F/ E8 L" ~% E 答:上面代码里的问题在于函数gets的使用,' J/ m' s9 _/ C, P
这个函数从stdin接收一个字符串而不检查它所复制的缓存的容积,
1 X# ^4 B* w9 e ~这可能会导致缓存溢出。这里推荐使用标准函数fgets代替。: V4 ^2 W' g" d' N; }" q, `1 R
2.strcpy函数0 E- A: Q: {* z; @
3 d2 h. u1 z( m5 J$ V
问:下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?* O$ `5 @- q% x1 S5 a
答:破解上述加密的关键在于利用攻破strcpy函数的漏洞。所以用户在向“passwd”缓存输入随机密码的时候并没有提前检查“passwd”的容量是否足够。所以,如果用户输入一个足够造成缓存溢出并且重写“flag”变量默认值所存在位置的内存的长“密码”,即使这个密码无法通过验证,flag验证位也变成了非零,也就可以获得被保护的数据了。例如:, s. h& R$ N% \4 O! b' K! |: x" S$ i
虽然上面的密码并不正确,但我们仍然可以通过缓存溢出绕开密码安全保护。
8 e' J" S& Y* Q4 ~$ P& I要避免这样的问题,建议使用 strncpy函数。4 {) g) B0 f0 q( }: Q) b) G
注:最近的编译器会在内部检测栈溢出的可能,所以这样往栈里存储变量很难出现栈溢出。在我的gcc里默认就是这样,所以我不得不使用编译命令‘-fno-stack-protector’来实现上述方案。, K' k% V- e, F% u% ]
3.main的返回类型
L' S k9 V+ r/ X
! i) d: ~# s/ S5 @2 S问:下面的代码能 编译通过吗?如果能,它有什么潜在的问题吗?
! J1 K& ]' k2 Q( `$ M& `' Q0 x 答:因为main方法的返回类型,这段代码的错误在大多数编译器里会被当作警告。main的返回类型应该是“int”而不是“void”。因为“int”返回类型会让程序返回状态值。这点非常重要,特别当程序是作为依赖于程序成功运行的脚本的一部分运行时。
; U7 p3 A/ p: G; k: k) A4.内存泄露+ p$ X' g# c/ m, ?
) ?$ y: A3 J* x! {4 Q0 A- B问:下面的代码会导致内存泄漏吗?
# u6 v3 ]1 g1 B 答:尽管上面的代码并没有释放分配给“ptr”的内存,但并不会在程序退出后导致内存泄漏。在程序结束后,所有这个程序分配的内存都会自动被处理掉。但如果上面的代码处于一个“while循环”中,那将会导致严重的内存泄漏问题!
0 g2 ^- y& O: w6 x; j提示:如果你想知道更多关于内存泄漏的知识和内存泄漏检测工具,可以来看看我们在Valgrind上的文章。# \- u! d0 {) l3 e6 N
5.free函数
* f0 o0 Q% _2 |2 x/ s
( Y) z$ V/ k& B% \8 ^3 S _2 F问:下面的程序会在用户输入'freeze'的时候出问题,而'zebra'则不会,为什么?
9 D) B' D' C1 A+ ?+ u7 v8 u 答:这里的问题在于,代码会(通过增加“ptr”)修改while循环里“ptr”存储的地址。当输入“zebra”时,while循环会在执行前被终止,因此传给free的变量就是传给malloc的地址。但在“freeze”时,“ptr”存储的地址会在while循环里被修改,因此导致传给free的地址出错,也就导致了seg-fault或者崩溃。: @1 F6 v& B& d" b1 o- d+ w _
6.使用_exit退出1 X1 N% ~" h, `9 a: S% j+ ]* P
( y' z! j- s$ A7 ^, K- j; l
问:在下面的代码中,atexit并没有被调用,为什么?
2 P4 ^7 l* x5 _ 这是因为_exit函数的使用,该函数并没有调用atexit等函数清理。如果使用atexit就应当使用exit或者“return”与之相配合。
4 U7 g5 s& n d7 p5 w" Y7.void*和C结构体
. ]* g2 F4 i* a. k* \- s/ ^
3 Y( F: c3 F1 y问:你能设计一个能接受任何类型的参数并返回interger(整数)结果的函数吗?+ u- p6 {3 m1 Z& E
答:如下:
( T1 C3 J- E9 E- y9 \0 h, C6 F如果这个函数的参数超过一个,那么这个函数应该由一个结构体来调用,这个结构体可以由需要传递参数来填充。
" ~: }) z4 \5 \# o8 N6 k* t8.*和++操作
( q N" W4 a7 b8 S
7 W$ ]' k: ]% N3 K0 Z8 `5 k7 `3 K问:下面的操作会输出什么?为什么?8 }6 `3 d* [0 z4 v) Y) ]! _9 z
答:输出结果应该是这样:, `0 N$ v; m' h
因为“++”和“*”的优先权一样,所以“*ptr++”相当于“*(ptr++)”。即应该先执行ptr++,然后才是*ptr,所以操作结果是“L”。第二个结果是“i”。
' I! H9 j) N. f E9.修改代码片段(或者只读代码)
- X4 O+ |6 T2 G" X
8 ~) l/ R5 l C) S# h1 q问:下面的代码段有错,你能指出来吗?
C; F8 U. s1 P 答:这是因为,通过*ptr = ‘T’,会改变内存中代码段(只读代码)“Linux”的第一个字母。这个操作是无效的,因此会造成seg-fault或者崩溃。
: e4 O( ^1 E0 j' o: e& P* {7 t10.会改变自己名字的进程
1 m# `$ e+ y" Y( [. ]& q) W, H" c1 q7 ]
问:你能写出一个在运行时改变自己进程名的程序吗?
; D, p& f5 u5 w3 l. l9 `9 v答:参见下面这段代码:" n G& b4 b+ B
11.返回本地变量的地址, w& S" W5 y2 U- Q" c* o; g i
- {) u6 g% c% X6 I/ k B
) x' \1 M4 s$ t x8 `
问:下面代码有问题吗?如果有,该怎么修改? s$ u, H+ q! \/ m
答:尽管上面的程序有时候能够正常运行,但是在“inc”中存在严重的漏洞。这个函数返回本地变量的地址。因为本地变量的生命周期就是“inc”的生命周期,所以在inc结束后,使用本地变量会发生不好的结果。这可以通过将main中变量“a”的地址来避免,这样以后还可以修改这个地址存储的值。2 D9 D- W9 Y& i; V3 `2 \
12.处理printf的参数# [( R B; a, P w# {, i9 C+ F: \
% C! X1 `! p# n3 n4 u# e! Y问:下面代码会输出什么?
! W1 A q* u4 A2 U" ]. e 答:输出结果是:
( ]% f, X9 B& n) N
* W& B7 T$ n+ n F9 l/ [: O8 _! x2 U5 u! j
这是因为C语言里函数的参数默认是从右往左处理的,输出时是从左往右。
4 d+ i) E! u+ F. l/ V! B, Z- l7 l 另外如果你想学c/c++,笔者这里介绍一个C/C++学习的零基础成长圈子Q羊君,⑤⑥⑨,②⑥⑧,③⑥⑦,要是你对C语言、C++或者是算法方面有兴趣的话,不管你是大牛还是小白,大家都一起成长进步。1 J/ b* j0 h6 ^# Z1 k

0 t4 A* u( w) H8 B. Q; b# S- b/ [来源:http://www.yidianzixun.com/article/0MaygqsN9 q+ L+ x/ r0 N2 F% J
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|