|
|
12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,看看你能做出几个!% m1 [* {/ s0 Q/ s4 Q8 U2 ?. j
1.gets函数
3 t! N/ T; `1 c9 X7 T- x: K. j& h
问:请找出下面代码里的问题:' [2 G" \8 d0 Q3 ?: s, h2 i2 M
答:上面代码里的问题在于函数gets的使用,1 v( F! K! Q' v7 O" ~
这个函数从stdin接收一个字符串而不检查它所复制的缓存的容积,; g$ c# y5 \5 i4 ]; E# f, r" M
这可能会导致缓存溢出。这里推荐使用标准函数fgets代替。# a& ^+ ^: ]" q, h+ C
2.strcpy函数6 P4 _/ A( z2 f
$ @5 W$ }6 G/ @2 O8 T, l: j$ W
问:下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?0 u3 n! N. R5 C0 _+ Z% k
答:破解上述加密的关键在于利用攻破strcpy函数的漏洞。所以用户在向“passwd”缓存输入随机密码的时候并没有提前检查“passwd”的容量是否足够。所以,如果用户输入一个足够造成缓存溢出并且重写“flag”变量默认值所存在位置的内存的长“密码”,即使这个密码无法通过验证,flag验证位也变成了非零,也就可以获得被保护的数据了。例如:, o' i, i! x8 V" c
虽然上面的密码并不正确,但我们仍然可以通过缓存溢出绕开密码安全保护。; A% d5 Q; a6 s( G9 T p5 J5 Y$ y
要避免这样的问题,建议使用 strncpy函数。( q! e* c/ R1 g! F( v
注:最近的编译器会在内部检测栈溢出的可能,所以这样往栈里存储变量很难出现栈溢出。在我的gcc里默认就是这样,所以我不得不使用编译命令‘-fno-stack-protector’来实现上述方案。
; t2 l: Q8 d& v: Y3.main的返回类型* R1 k$ V7 K4 Y
4 O* P2 c" Y1 r0 H) m& U8 u# c
问:下面的代码能 编译通过吗?如果能,它有什么潜在的问题吗?
8 w6 W( [7 p) o" i3 s( L8 U 答:因为main方法的返回类型,这段代码的错误在大多数编译器里会被当作警告。main的返回类型应该是“int”而不是“void”。因为“int”返回类型会让程序返回状态值。这点非常重要,特别当程序是作为依赖于程序成功运行的脚本的一部分运行时。
5 |0 e( t j# S7 x3 t5 b4.内存泄露
; k5 I( o% i2 u
; f. h' u) ^* c! D4 z( Z/ \+ ]问:下面的代码会导致内存泄漏吗?9 T& {4 \& ]7 ? g( ^$ g
答:尽管上面的代码并没有释放分配给“ptr”的内存,但并不会在程序退出后导致内存泄漏。在程序结束后,所有这个程序分配的内存都会自动被处理掉。但如果上面的代码处于一个“while循环”中,那将会导致严重的内存泄漏问题!3 g! q+ s: O! O+ Y; q2 K+ E, b
提示:如果你想知道更多关于内存泄漏的知识和内存泄漏检测工具,可以来看看我们在Valgrind上的文章。% U8 |; A* Z/ W+ [" @$ ^+ |2 e/ P
5.free函数8 z9 \! n% b3 I W" |
: F. p1 I2 G5 O2 {3 a& s l问:下面的程序会在用户输入'freeze'的时候出问题,而'zebra'则不会,为什么?8 K' u5 `) Q# ^0 O4 K* s
答:这里的问题在于,代码会(通过增加“ptr”)修改while循环里“ptr”存储的地址。当输入“zebra”时,while循环会在执行前被终止,因此传给free的变量就是传给malloc的地址。但在“freeze”时,“ptr”存储的地址会在while循环里被修改,因此导致传给free的地址出错,也就导致了seg-fault或者崩溃。" d3 E5 ~$ h, M& A: a$ s4 k
6.使用_exit退出7 v( D. K k' b m8 j) Q+ d2 Q) @
3 l, w' `3 L d. m m0 o: b问:在下面的代码中,atexit并没有被调用,为什么?
8 ]' ?; J a6 u6 I/ z; ?) f 这是因为_exit函数的使用,该函数并没有调用atexit等函数清理。如果使用atexit就应当使用exit或者“return”与之相配合。
& t, [* B; _! t) S7.void*和C结构体
4 L6 w1 J/ n" v0 Y. N, u
6 L+ E7 _, g# O$ h8 ?6 J0 c- O问:你能设计一个能接受任何类型的参数并返回interger(整数)结果的函数吗?3 \6 I2 `9 i( v% p' v" G u9 S6 ]
答:如下:
- _4 S1 ^0 O' G8 m# p* t! B, J如果这个函数的参数超过一个,那么这个函数应该由一个结构体来调用,这个结构体可以由需要传递参数来填充。 W. t" p' ~2 {8 w; n* ?
8.*和++操作
9 w2 V8 U4 v' L: w
1 i# a8 r. t! u) d问:下面的操作会输出什么?为什么?$ m6 V. t* v* k" r ~7 V
答:输出结果应该是这样:& Z/ e) t/ _7 J+ @$ H$ v
因为“++”和“*”的优先权一样,所以“*ptr++”相当于“*(ptr++)”。即应该先执行ptr++,然后才是*ptr,所以操作结果是“L”。第二个结果是“i”。. T$ d; c" w# V L
9.修改代码片段(或者只读代码)
) U. t( U2 i& c, Y% ^+ F, P9 v! Z4 `, u `" K7 Z* ]
问:下面的代码段有错,你能指出来吗?
+ J: ?& q, B' L- y 答:这是因为,通过*ptr = ‘T’,会改变内存中代码段(只读代码)“Linux”的第一个字母。这个操作是无效的,因此会造成seg-fault或者崩溃。! T; E0 } H5 C. `3 m3 G
10.会改变自己名字的进程
7 G0 N& I; V+ F: z. _5 c/ b0 `/ y6 c9 T' B
问:你能写出一个在运行时改变自己进程名的程序吗?- {1 f& F# r3 t Z
答:参见下面这段代码:+ {% c* Y |3 j; p! R. H2 ?! z
11.返回本地变量的地址
. \) A6 i, ~" D4 q" ^# z. y& g
* M& C% ?* R5 t& _# l* [
# O4 e8 _7 B: d" `; L6 W/ M; |0 [7 k# b问:下面代码有问题吗?如果有,该怎么修改?, C) D2 X/ n% Q
答:尽管上面的程序有时候能够正常运行,但是在“inc”中存在严重的漏洞。这个函数返回本地变量的地址。因为本地变量的生命周期就是“inc”的生命周期,所以在inc结束后,使用本地变量会发生不好的结果。这可以通过将main中变量“a”的地址来避免,这样以后还可以修改这个地址存储的值。& x2 g# z) {9 Y! c
12.处理printf的参数; Y- Z( H& s8 C
& u* S$ M) ~9 L. a/ c问:下面代码会输出什么?. c) o" u$ J! |. n
答:输出结果是:
( V+ I7 S7 O3 @
& p7 l8 m9 Y" {: F0 F- x
5 t0 w9 U7 d* T这是因为C语言里函数的参数默认是从右往左处理的,输出时是从左往右。
5 \1 K1 K" x# ?) n0 B 另外如果你想学c/c++,笔者这里介绍一个C/C++学习的零基础成长圈子Q羊君,⑤⑥⑨,②⑥⑧,③⑥⑦,要是你对C语言、C++或者是算法方面有兴趣的话,不管你是大牛还是小白,大家都一起成长进步。& a6 W- L$ }5 a. Q: J6 o7 w9 ^
' ^, P# V( Q! I. V
来源:http://www.yidianzixun.com/article/0MaygqsN
3 X: ^3 m5 ~8 r$ Z( {" y免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|