|
|
12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,看看你能做出几个!
* `9 ^- X# A2 [9 m1.gets函数
# t( O, N1 m. L0 L. _/ N9 f( _! ]' z
! Q6 e. v! r9 u3 Y f问:请找出下面代码里的问题:
. O" @0 W# U2 ` h 答:上面代码里的问题在于函数gets的使用,: d* \' i, Z5 N" d, G+ A" ~; V7 Q r
这个函数从stdin接收一个字符串而不检查它所复制的缓存的容积,* p1 d' D+ R& A6 j! O$ r
这可能会导致缓存溢出。这里推荐使用标准函数fgets代替。
" q0 c+ S- t; Z' C! E _2.strcpy函数- t- H ]# T) V" t1 a
% A0 o3 n7 b2 h2 @+ l3 M2 n
问:下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?
+ Q* w. j9 [ S! R# k' K ^6 g3 v 答:破解上述加密的关键在于利用攻破strcpy函数的漏洞。所以用户在向“passwd”缓存输入随机密码的时候并没有提前检查“passwd”的容量是否足够。所以,如果用户输入一个足够造成缓存溢出并且重写“flag”变量默认值所存在位置的内存的长“密码”,即使这个密码无法通过验证,flag验证位也变成了非零,也就可以获得被保护的数据了。例如:* t9 V( u+ @% r0 |5 w2 s
虽然上面的密码并不正确,但我们仍然可以通过缓存溢出绕开密码安全保护。
2 o2 O) g5 S6 ?- l: g3 m要避免这样的问题,建议使用 strncpy函数。
7 j) n4 X4 C8 N9 Z3 p# N注:最近的编译器会在内部检测栈溢出的可能,所以这样往栈里存储变量很难出现栈溢出。在我的gcc里默认就是这样,所以我不得不使用编译命令‘-fno-stack-protector’来实现上述方案。
. v1 K% G; Q* b' V; ^* F3.main的返回类型9 N5 G' N& _0 w; F3 ?
4 i: f$ C* O0 K问:下面的代码能 编译通过吗?如果能,它有什么潜在的问题吗?2 W6 _% V' n/ C8 W
答:因为main方法的返回类型,这段代码的错误在大多数编译器里会被当作警告。main的返回类型应该是“int”而不是“void”。因为“int”返回类型会让程序返回状态值。这点非常重要,特别当程序是作为依赖于程序成功运行的脚本的一部分运行时。
. D3 C: d# {; s" ?4.内存泄露9 X$ b% a9 U0 r, m( ^0 l4 y, F1 N' z9 M
$ J) V3 w$ \3 s! f5 C
问:下面的代码会导致内存泄漏吗?$ w( m" P3 z' R6 Y \3 a
答:尽管上面的代码并没有释放分配给“ptr”的内存,但并不会在程序退出后导致内存泄漏。在程序结束后,所有这个程序分配的内存都会自动被处理掉。但如果上面的代码处于一个“while循环”中,那将会导致严重的内存泄漏问题!
* N( ]; N" ]- j7 R提示:如果你想知道更多关于内存泄漏的知识和内存泄漏检测工具,可以来看看我们在Valgrind上的文章。
; {1 _( J2 U( n. C Z( n: v5.free函数: P, m- z8 i7 g4 z4 [- [/ ^$ n
( n, S( Q7 u7 c$ ? g问:下面的程序会在用户输入'freeze'的时候出问题,而'zebra'则不会,为什么?
% I8 u: \! [9 s 答:这里的问题在于,代码会(通过增加“ptr”)修改while循环里“ptr”存储的地址。当输入“zebra”时,while循环会在执行前被终止,因此传给free的变量就是传给malloc的地址。但在“freeze”时,“ptr”存储的地址会在while循环里被修改,因此导致传给free的地址出错,也就导致了seg-fault或者崩溃。- C: k0 H8 c d
6.使用_exit退出
3 C- `9 h* V8 ~; U. V
& X- \" I3 d( w: f/ K2 G& a' t: V问:在下面的代码中,atexit并没有被调用,为什么?! |' z4 O0 J4 k+ W$ `( @" n
这是因为_exit函数的使用,该函数并没有调用atexit等函数清理。如果使用atexit就应当使用exit或者“return”与之相配合。2 U& j( D8 H# S3 _7 ^
7.void*和C结构体5 O) R5 {2 H# R; C( z( h
' F' K$ R) x/ X# B问:你能设计一个能接受任何类型的参数并返回interger(整数)结果的函数吗?9 C& _' Z4 j3 y# G% B
答:如下:
& P0 H7 h$ U/ G6 w% R6 t如果这个函数的参数超过一个,那么这个函数应该由一个结构体来调用,这个结构体可以由需要传递参数来填充。
6 j$ R: g4 [' T5 u; |2 ~8.*和++操作
" W5 @7 ~$ O1 Z, ~- a: E! _( P
2 Z& ]3 Q/ x& h1 b2 K9 W问:下面的操作会输出什么?为什么?
" ?& p- Q b. R. E 答:输出结果应该是这样:/ [+ b: k( c( A" k/ J
因为“++”和“*”的优先权一样,所以“*ptr++”相当于“*(ptr++)”。即应该先执行ptr++,然后才是*ptr,所以操作结果是“L”。第二个结果是“i”。
& ]. ^6 Z# i G& H9 g1 ~- S( [9.修改代码片段(或者只读代码)
8 L8 Y6 r3 F. m( s
. Y$ \& z+ J) |% A1 g' ^问:下面的代码段有错,你能指出来吗?
' E0 E, _0 V. N 答:这是因为,通过*ptr = ‘T’,会改变内存中代码段(只读代码)“Linux”的第一个字母。这个操作是无效的,因此会造成seg-fault或者崩溃。$ k J! O3 B$ s3 ~& z; A9 @
10.会改变自己名字的进程5 Y, o' C4 w1 W8 x `
- x$ T+ U& X: m5 H
问:你能写出一个在运行时改变自己进程名的程序吗?2 F8 r# K; J- F: b; ?
答:参见下面这段代码:
8 i' Z! c3 f- b0 X4 N1 A5 g 11.返回本地变量的地址
/ ] c& @! s- `1 f- l7 l% K
) b% N/ k0 H8 e! `
! E2 l& N: c0 W( U5 e) ~: k问:下面代码有问题吗?如果有,该怎么修改?. G6 @ g, b$ l% o/ @
答:尽管上面的程序有时候能够正常运行,但是在“inc”中存在严重的漏洞。这个函数返回本地变量的地址。因为本地变量的生命周期就是“inc”的生命周期,所以在inc结束后,使用本地变量会发生不好的结果。这可以通过将main中变量“a”的地址来避免,这样以后还可以修改这个地址存储的值。
7 u: [/ t, }. a9 [12.处理printf的参数2 D% ^) _& W3 Q" B8 h0 x5 N& w
- ]1 M- s6 u3 Y a8 j6 @/ L1 w
问:下面代码会输出什么?0 o9 F$ U$ a9 n
答:输出结果是:
, O+ x5 M% M Z. ^
2 t' u5 M; x- d f1 I( T, i1 {9 b7 L p7 \' E
这是因为C语言里函数的参数默认是从右往左处理的,输出时是从左往右。
! b& F+ J/ G- d2 a- ]1 f 另外如果你想学c/c++,笔者这里介绍一个C/C++学习的零基础成长圈子Q羊君,⑤⑥⑨,②⑥⑧,③⑥⑦,要是你对C语言、C++或者是算法方面有兴趣的话,不管你是大牛还是小白,大家都一起成长进步。
" {- Q& p9 Q4 k - L/ C2 M* T& A) k
来源:http://www.yidianzixun.com/article/0MaygqsN& n# I/ F9 M4 x7 M( \$ R. E* n* `
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|