|
本文展示了14个C语言的迷题以及答案,代码应该是足够清楚的,而且有相当的一些例子可能是我们日常工作可能会见得到的。通过这些迷题,希望你能更了解C语言。 ) V( `+ Z% q5 J
如果你不看答案,不知道是否有把握回答各个谜题?让我们来试试。
( i7 W' I& l7 V! m* z, n / k3 b: m( L: h- C- \
1
$ R+ d9 ]9 V6 I" G, ]
& {9 a# O) L8 `
下面的程序并不见得会输出"hello-std-out",你知道为什么吗?
% L% s, P6 B$ A$ P6 p- #include# p" L' v0 z9 h, k
- #include
9 h+ I8 Z# K. `: Z) h" ~ - int main()
$ Y9 ~- F/ L% Z( c - {
- K/ z7 X" O$ Q$ g) N- m - while(1)
! ^% ^3 _9 P# S% p% k) M+ J( G - {
! v3 @* s! E- p! O- L! H" H - fprintf(stdout,"hello-std-out");! Q" ~# ~3 J, z$ Z% ]
- fprintf(stderr,"hello-std-err");5 B# T4 c, H' E7 j9 J
- sleep(1);
' r' I4 K; l0 Z7 Z( c - }
& b! d9 |4 \4 p9 }5 E - return 0;
: i: T3 q) V" N2 n6 c0 v0 U( _ - }
复制代码
4 Y3 E# Z1 O# N E x, P- x) S; E
: e1 Y' V: E7 m9 s$ l参考答案 stdout和stderr是不同设备描述符。stdout是块设备,stderr则不是。对于块设备,只有当下面几种情况下才会被输入:遇到回车;缓冲区满;flush被调用。而stderr则不会。 ; u: T# Z ^5 d" |
1 e+ U) A) c- K( f4 [+ g
2
6 Y. v' d' V2 q+ G6 c% v. ~. f5 `5 B% x8 R" W* v
下面的程序看起来是正常的,使用了一个逗号表达式来做初始化。可惜这段程序是有问题的。你知道为什么吗? - : b' ]. A8 K$ p) g( K; G
- #include
0 b! Q% e9 p; T I) A+ D# k - int main()
& w" H& n; R* R$ c - {' S7 W. \" f) i0 B
- int a = 1,2;
! g9 {% Q/ i. C# M - printf("a : %d\n",a);
4 U# l; k( \. X - return 0;
9 U7 F- U& l+ o6 N) J! [ - }
复制代码
; G3 f$ N, l1 r; u) t c: V) c: Z" u- _4 G0 }5 f
参考答案 这个程序会得到编译出错(语法出错)。逗号表达式是没错,可是在初始化和变量声明时,逗号并不是逗号表达式的意义。这点要区分,要修改上面这个程序,你需要加上括号:"int a = (1,2);"。 * ?& y. l# P- X7 M7 S
3 ~- }" a# J0 O" a
3
7 A% w3 A* H7 f! g0 s! K/ \4 ?
# |4 ^* G% d. Y2 j0 W5 M }
下面的程序会有什么样的输出呢? - 8 j: W0 f: i* v! h" M
- #include/ ]- p% A/ `4 U, a p( g
- int main()6 c! Y3 x. T( I- v# `
- {2 x1 @2 Q+ k( Q7 F6 Z0 y
- int i=43;
) ]; m" ~4 i+ p - printf("%d\n",printf("%d",printf("%d",i)));
$ O3 V3 p5 F6 k; h/ G& Y8 @3 ^ - return 0;* _3 \/ v9 ^9 N: X% ?
- }
复制代码
9 m8 ~/ i$ O5 w( i+ N4 Z* s5 ~: M参考答案 程序会输出4321,你知道为什么吗?要知道为什么,你需要知道printf的返回值是什么。printf返回值是输出的字符个数。
7 w- Y& {7 S& K, F 7 {: S# A$ b8 w
4 h, ?8 L' }, k5 {& E7 x' \: k
& v" A$ C0 A) E8 s, k! Y7 o
下面的程序会输出什么? - / o* K W, Z- L/ d& v2 B9 E2 q
- #include% _7 b! ~- z& s0 a4 Z$ T
- int main()+ P' r! ?0 Q4 Z9 f" h
- {1 o0 F: u K& A2 g+ U& M+ n
- float a = 12.5;
2 B0 F; h1 L8 j- J5 ? - printf("%d\n", a);8 M! Z3 f# w( J/ Z
- printf("%d\n", (int)a);
& c. n& n' e) j - printf("%d\n", *(int *)&a);& c) m, E$ b0 M: w$ v
- return 0;/ X. N( S- c! r7 l% M. l
- }
复制代码
4 w3 ^8 e1 [ O6 \. H$ A) r! R0 b. B参考答案 该项程序输出:"0 12 1095237632"。 7 ]. T% q" H) \
原因是:浮点数是4个字节,12.5f转成二进制是:01000001010010000000000000000000,十六进制是:0x41480000,十进制是:1095237632。所以,第二和第三个输出相信大家也知道是为什么了。 " l5 N* E% k" D; G3 @! \5 ~
而对于第一个,为什么会输出0,我们需要了解一下float和double的内存布局,如下:
* V( F5 r' ]: \8 @ • float: 1位符号位(s)、8位指数(e),23位尾数(m,共32位)。 • double: 1位符号位(s)、11位指数(e),52位尾数(m,共64位)。 ) P) s# j- t/ n
然后,我们还需要了解一下printf由于类型不匹配,所以,会把float直接转成double,注意,12.5的float和double的内存二进制完全不一样。别忘了在x86芯片下使用是的反字节序,高位字节和低位字位要反过来。所以: • float版:0x41480000 (在内存中是:00 00 48 41)。
( Z' x t! X5 w" m • double版:0x4029000000000000 (在内存中是:00 00 00 00 00 00 29 40)。2 r, s; _& \6 }
6 a% r5 X: K& M% }# _, o
而我们的%d要求是一个4字节的int,对于double的内存布局,我们可以看到前四个字节是00,所以输出自然是0了。这个示例向我们说明printf并不是类型安全的,这就是为什么C++要引如cout的原因了。 6 @7 J+ ?) y+ M; ~+ E+ y+ T
2 [; {% O2 A% M6 [7 S* `4 o7 d5: B5 D* M$ O8 M6 \2 C
3 z w& c) m! v6 u+ F! z
下面,我们再来看一个交叉编译的事情,下面的两个文件可以编译通过吗?如果可以通过,结果是什么?
5 ?) k- `: ], D( x- //file1.cint arr[80];
+ C' H6 N. G+ |6 | - //file2.cextern int *arr;
# e5 g l8 M6 _ Y/ ]9 n - int main()
6 ]+ r! _0 P% @1 x8 q - {
0 s1 j- S% T, U7 F* L, C; r' f - arr[1] = 100;( ~& C1 T& m, W( [7 g6 e
- printf("%d\n", arr[1]);
4 Q) q2 ^* g* d( @9 E - return 0;
* g) u* f) J C) y }/ A - }
复制代码 % N [8 R/ I3 v& ^1 I2 ]; o( Q
参考答案 该程序可以编译通过,但运行时会出错。为什么呢?原因是,在另一个文件中用 extern int *arr来外部声明一个数组并不能得到实际的期望值,因为他们的类型并不匹配。所以导致指针实际并没有指向那个数组。 * |8 [! Z% @7 j! |" w
注意:一个指向数组的指针,并不等于一个数组。
; [0 D1 R8 P" K" |, y a
修改:"extern int arr[]"。 2 O9 {. P1 O$ g
# N) I* j2 |" _& m& x4 I: L5 m$ b
60 q0 j. ]( g g& o' p, t0 A; x
. _( O2 w5 q; F: A
请说出下面的程序输出是多少?并解释为什么?(注意,该程序并不会输出"b is 20")
5 a7 ~1 I. A; W$ z& N9 j( n) k- #include
1 I8 E8 \: d! ^, ^2 t" ?3 N - int main()
0 I5 \* n* |7 o% v }; F* b - {( z7 w" N* I2 W: w: n
- int a=1;
9 N" G) a9 [ {7 H. f - switch(a)- ^* N8 I8 U4 b5 h" L& S
- {0 }# F' c8 x0 C
- int b=20;+ J+ M* A' c! S3 S
- case 1:
* `' B( k. {) U, Q - printf("b is %d\n",b);/ J! o- @ b& w
- break;
' m9 J; I/ f. C" p" L- i/ }- S2 ` - default:% W. i* n! v$ |. A2 s: t
- printf("b is %d\n",b);3 K2 v4 i5 R- M0 o# u7 [1 k
- break;" c+ `5 O# _7 X5 m4 U) S3 [& T
- }" o; _2 `: P. l$ [3 S. T
- return 0;' _9 U, [% u& d* H: R Z
- }
复制代码 9 M$ ^, t2 R3 l+ @2 g
参考答案 该程序在编译时,可能会出现一条warning: unreachable code at beginning of switch statement。我们以为进入switch后,变量b会被初始化,其实并不然,因为switch-case语句会把变量b的初始化直接就跳过了。所以,程序会输出一个随机的内存值。
( Z3 S. c1 y9 R5 d; e" S& @, E
7 v; m) E; ~8 N' d0 c' q72 D* E2 H- U& f3 l0 |+ o! H
' x5 Q9 K' w X
请问下面的程序会有什么潜在的危险?
! e" C# F8 c& |: W$ ?, {& g- Y- #include
$ k- e/ O4 ~* e5 B5 m% e - int main()/ c: A: j i3 o! c; ]! {$ _. H/ G
- {8 ^* \% L+ s3 X+ o
- char str[80];) ^8 k8 y9 D( N3 ]7 D
- printf("Enter the string:");5 W4 N$ H& h( E- O% o4 w
- scanf("%s",str);- Q7 T- ^3 g; Q; Q$ w" _; Q0 F+ y# a. x
- printf("You entered:%s\n",str);
5 k7 D. ?; X1 o K" Y- ` - return 0;
: E( S p0 j8 t2 h7 Q! `6 I# i+ m - }
复制代码 : B2 g8 N: @- k; [! V
参考答案 本题很简单了。这个程序的潜在问题是,如果用户输入了超过80个长度的字符,那么就会有数组越界的问题了,你的程序很有可能会crash了。
1 k" o8 s) W% i, E: c 7 O. Q, [6 v* D' T+ w2 z+ V
8
: k4 g& @, ~3 U5 d1 Z% E5 J
) f2 |# H' G9 V6 l" F/ x/ M
请问下面的程序输出什么?
; `. B- G+ b# T- #include0 o) l! o& I6 R& X
- int main()
; q# x; t4 K0 i - {
5 h3 A) |8 P( e; H/ H* X - int i;
- m; p% Q% e7 h4 F) D- J4 u6 ?( [ - i = 10;( m+ `- B1 F" V9 I8 n
- printf("i : %d\n",i);8 I7 r) K2 A9 r0 G/ a
- printf("sizeof(i++) is: %d\n",sizeof(i++)); s2 F$ |$ F8 W3 {
- printf("i : %d\n",i);; y7 c' l3 h0 `& C" s
- return 0;
+ W. Y; [2 i0 @ \ - }
复制代码
: L) Q: l/ d, @7 g: w% Y参考答案 如果你觉得输出分别是:10,4,11。那么你就错了。 & G4 b) z: V4 L4 X! a( f
错在了第三个,第一个是10没有什么问题,第二个是4,也没有什么问题,因为是32位机上一个int有4个字节。但是第三个为什么输出的不是11呢?居然还是10?原因是,sizeof不是一个函数,是一个操作符,其求i++的类型的size,这是一件可以在程序运行前(编译时)完全的事情,所以,sizeof(i++)直接就被4给取代了,在运行时也就不会有了i++这个表达式。 - X. u5 ]) P- d0 I; G( W8 e
9 ~* K) v! {* j+ E
9
. p0 Y+ @( c! N0 ]8 ~4 Z# x9 |
4 p) l! \& f8 S
请问下面的程序的输出值是什么?
$ i/ o5 X/ x# o& b( ?- U- z5 x- #include
0 F/ G; z2 ]# V$ d& \$ ^ - #include6 |0 x3 D0 V8 J H* o# {
- 3 O3 T# T/ ~* r+ L! G
- #define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0]))
! d; z+ M! U w - #define PrintInt(expr) printf("%s:%d\n",#expr,(expr))$ [' ]7 b3 @8 X# l4 \
- 0 s# n: j0 _5 ?; Z) h: `4 l
- int main()" I) V% \+ f2 l/ L# w- ~5 i
- {# a4 l/ R9 `4 i& t+ }9 S. y, F% Z
- /* The powers of 10 */
3 M- ]9 Q0 F4 |. g' m# X - int pot[] = {
& Z7 [$ P" o9 J& Y3 E8 g! k2 ]- c: O5 I - 0001,6 v3 k% e+ h- o
- 0010,( T- y/ Q" `/ p4 |
- 0100,6 z( s6 ]6 q* q: q( ~ g' {
- 1000) ~! W" T% m0 X. H" S
- };7 z3 A# e, |! I ^1 i V9 [
- * }6 p' h! h5 f y
- int i;6 i# X6 S6 d2 J7 w/ |
- for(i=0;i<SIZEOF(pot);i++)PrintInt(pot[i]);
3 N) I" Y+ t7 C& Z+ s - return 0;! P$ s2 r! Z0 `, L$ d0 O
- }
复制代码
8 ]; N! {9 n& M# v4 V参考答案 如果你对于PrintInt这个宏有问题的话,可以去看一看资料。不过,本例的问题不在这里,本例的输出会是:1,8,64,1000。其实很简单了,在C/C++中,以0开头的数字都是八进制的。
: h: o1 x* v4 q, @
: P4 I8 A) p, `8 Q104 v# v+ S+ X' w* Z; B" i& i
( C( Y& l6 u1 |8 e+ ?2 _# u" T
请问下面的程序输出是什么?(绝对不是10)
8 h1 R7 _: k( ?2 s2 Z+ F* i, V8 p- #include0 F) g6 `/ ]* {0 n! x' a
- #define PrintInt(expr) printf("%s : %dn",#expr,(expr))
7 J) @4 H* Y# K% K9 V! p" n - int main()3 F- |& G+ ?% O8 G6 T6 _% P
- {+ ? U( I0 I9 y! x7 u
- int y = 100;" N. a& J% T+ Q+ S* {
- int *p;0 e6 _& l( o2 ]9 K9 D1 ~. W/ w
- p = malloc(sizeof(int));5 f* `! L# P3 J
- *p = 10;! `; a$ F0 a! z: r1 M/ h
- y = y/*p; /*dividing y by *p */;6 O- N6 F/ H" h/ Q: w8 @% p$ ^
- PrintInt(y);% S5 w' F, h8 D+ [
- return 0;
' q+ D, M0 m8 N - }
复制代码
$ U9 {$ m B) m. K% S参考答案 本题输出的是100。为什么呢?问题就出在"y = y/*p;"上了,我们本来想的是"y / (*p)",然而,我们没有加入空格和括号,结果"y/*p"中的"/*"被解释成了注释的开始。于是,这也是整个恶梦的开始。
, Z8 @+ [* q; ?2 G, n# M" v/ P% P
2 o: U2 D7 T3 v$ |0 A- L! g4 x11' ~; n/ A" ?# X) v* d. k
* C) g- t; W2 e( {
下面的输出是什么? - 1 ~5 M7 w& M% L! s3 Q m
- #include% E0 U5 e1 T/ `) G1 X" g8 l
- int main()7 a+ H9 Y! P3 }- S
- {$ C" E) W$ E* C, v; n' J
- int i = 6;
# H0 ?2 v/ p5 ~7 c$ w, ^ - if( ((++i < 7) && ( i++/6)) || (++i <= 9));
' w+ I* E# e, X - printf("%d\n",i);
( p0 C. Y5 O) u/ F( Z8 u( P - return 0;$ ^, _4 ], u+ x A+ H4 I, Y
- }
复制代码 7 _- r. Z1 B! U8 X S% Z
参考答案 本题并不简单的是考前缀++或反缀++,本题主要考的是&&和||的短路求值的问题。 5 x- ]# d* X! {) i
所谓短路求值:对于(条件1 && 条件2),如果“条件1”是false,那“条件2”的表达式会被忽略了。对于(条件1 || 条件2),如果“条件1”为true,而“条件2”的表达式则被忽略了。
7 F5 T2 }2 \) q4 b# f4 C
所以,我相信你会知道本题的答案是什么了。
3 F3 E9 G8 X! `# C8 z* d( _ S 3 Q9 G& Z0 \6 h4 }
12; A- N2 B$ q8 }9 n- t
! d" S9 p6 m" Y
下面的C程序是合法的吗?如果是,那么输出是什么?
' k% W/ H5 N5 Q# }1 t. u6 w- #include/ @# T" d% u7 M, d! y* c- M8 a- j
- int main()% T' ~* y5 d- u; j1 J7 }! {6 z
- {: ?' e: i! m1 a: \3 D4 r2 Z
- int a=3, b = 5;& ^0 K6 X1 Y$ g+ E2 E" M! J5 k% E
- printf(&a["Ya!Hello! how is this? %s\n"], &b["junk/super"]);9 p8 x" i5 z' o: g1 S" x2 R* |
- printf(&a["WHAT%c%c%c %c%c %c !\n"], 1["this"],2["beauty"],0["tool"],0["is"],3["sensitive"],4["CCCCCC"]);
, Z1 [: z; a+ M8 R' u - return 0;6 O8 |# y& ]/ b$ {
- }
复制代码
4 v3 J9 z% ]9 W" v2 F/ \, w+ `' o参考答案 本例是合法的,输出为:"Hello! how is this? super That is C !" & Q; s1 S# r K
本例主要展示了一种另类的用法。下面的两种用法是相同的: • "hello"[2] • 2["hello"]
/ R6 S& Q" y9 `/ n" S. \
如果你知道:a 其实就是 *(a+i)也就是 *(i+a),所以如果写成 i[a] 应该也不难理解了。
0 v$ V2 \+ M& j7 t% k& Y: s- u/ K ; \" T! A/ g) [% g& c
134 B9 P/ G1 \0 ~; G _" J! P
7 N5 D6 b# s: G: K: e/ R
请问下面的程序输出什么?(假设:输入"Hello, World")
* _. C2 ?8 [+ k) Q- #include
: L* ^% f3 K$ H4 r T - int main()% b5 D/ m1 v. p/ i+ u8 a% M
- {
0 J; N5 A1 n$ {3 v" X - char dummy[80];
. t9 S/ C4 \7 u( z - printf("Enter a string:\n");* h8 N$ J1 T* O2 b/ ~
- scanf("%[^r]",dummy);% ~4 e# T* B4 _3 X& ^
- printf("%s\n",dummy);6 g. p, j# H' e" h- G* h6 n
- return 0;
4 J& @! i* e+ P - }
复制代码 $ m, L4 T/ r% P) \
参考答案 本例的输出是"Hello, Wo"。scanf中的"%[^r]"是从中作梗的东西,意思是遇到字符r就结束了。
: ], O: |& e1 j( L) X' P% J ' Z, C0 [+ t7 v( a+ ]
146 m% f) p9 V( w. n8 S# J
: U q% h& G; Q4 I
下面的程序试图使用"位操作"来完成"乘5"的操作,不过这个程序中有个BUG,你知道是什么吗? - 3 Q+ w$ {0 A0 C) r4 U
- #include6 G4 ?' \% D7 N. n
- #define PrintInt(expr) printf("%s : %d\n",#expr,(expr))# \" O5 x. _& q* o( t
- int FiveTimes(int a)
2 z& v( l; l! ?- }8 L8 T - {% _9 J8 z$ D4 p1 q- H
- int t;( [7 b- N( l! z) c4 ?* J" A
- t = a<<2 + a;2 p2 c: t. F- r# Y
- return t;1 e; y' Q0 W# {) h
- }1 w. B8 B7 I' ?3 r A+ L5 N
- int main()* k; p& s4 _, d% d0 n
- {
6 z! O6 i' Y# Q2 A6 C - int a = 1, b = 2,c = 3;: n7 n( {+ m1 W' f. Y4 p5 \' z6 q
- PrintInt(FiveTimes(a));! Y5 c, V5 y7 P
- PrintInt(FiveTimes(b));
4 V/ R. T( A) n# N( a - PrintInt(FiveTimes(c));) {9 M: }, |" F' }1 U: k7 R5 l1 p
- return 0;5 N5 T! H3 T+ n" X
- }
复制代码 : N7 A5 Q+ M9 H6 K# ?0 `5 T
参考答案 本题的问题在于函数FiveTimes中的表达式"t = a<<2 + a;"。对于a<<2这个位操作,优先级要比加法要低,所以这个表达式就成了"t = a << (2+a)",于是我们就得不到我们想要的值。
; T7 d' m, F* M
该程序修正如下:
% |1 C5 |2 l3 V% x7 _7 r3 H% X- int FiveTimes(int a)
}* s* B6 M3 G% X7 [, M; W - {
2 n8 Q! k5 I0 @+ @( h/ ^. c3 l - int t;
( `$ X# H* s9 L1 `& C8 K; O - t = (a<<2) + a;, c8 U; W! Y' t1 J2 f3 I
- return t;
9 s, _3 s( {8 [5 u# l9 v% U: X* ? - }
复制代码 % I5 B* z6 h d. |" m1 R# {+ k4 ~, g
|