程序员都知道,也都会使用printf函数,但你知道它也有“安全隐患”吗?
: X, s/ ^/ E8 F5 r0 n9 y4 n& Q
下面就来举例我说说:
* N6 Y" s1 H2 Y$ U
嵌入式专栏 1 问题描述打印输出的数据并不是理论值,如下图(右边):* O5 u3 P$ o! T; y
* ~! K- H; w; @
嵌入式专栏 2 进一步描述问题请细致注意看下面代码,有如以下奇怪的现象:
3 R' F4 i, s5 F( d0 [- int a=5;
, P7 b4 h0 B X8 v5 L - floatx=a; //这里转换是没有问题的。%f打印x是 5.000000
+ l3 k& u( h- N) a6 v
0 o0 }. h# f2 ?" ]( O- printf("%d\n",a);
) k9 s& B) G. l; _) a1 y$ _5 s. Y - printf("%f\n",a); //输出为什么是0.000000?-----问题1, j3 a8 x4 I7 q& w1 r1 Z8 H9 {. }
- printf("%f\n",x);: E! u. ~4 k& `, B9 Y
- printf("%d\n",x); //输出为什么是0?-----问题2, L, i, y5 Z) H; q
- printf("%f,%f\n",a,x); //输出都是0.000000 为什么? ----问题3
! k" t( l, a2 h7 C - printf("%f,%f\n",x,a); //调换一下a,x的顺序,正常了,为什么?----问题4
' b% y0 z% C: s' t' H - printf("%d,%f\n",a,x);! \. j+ H( [) m& ?: F
- * W( N+ S" ^5 L. @% w. g/ N
- getchar();
* C6 Z0 ]6 b% v& ^; t8 t - return0;
复制代码
% }% M9 } X* g$ \& [2 e6 x这里有四个问题,下面会进行解答。7 t/ n T/ D0 w' m. e
& j& ?. J/ o! Q& Y嵌入式专栏 3 printf()函数的原理解释明确这些问题首先须要明确printf()函数的工作原理。 P; ?! v4 y' _5 r# M( a2 \; s
printf()维持了一个需要打印的变量栈,默认情况下,參数进栈的顺序是由右向左的。因此,參数进栈以后的内存模型例如以下图所看到的:
) Y% u1 J; j' _9 Y8 j
打印的时候,printf依照字符转换说明符规定的格式从低地址開始提取数据。直到參数打印完。
1 E2 m5 i4 Y" t' ?9 L( l比方遇到 %f 说明符就提取8个字节的数据,遇到 %d 就提取4个字节。printf()事实上不知道參数的个数,它仅仅会依据format中的打印格式的数目依次打印堆栈中參数format后面地址的内容。
B) B6 v5 O, m+ Y" A4 L8 o8 T! |这样一来,printf()事实上存在安全隐患:它会强行读取内存的数据当作正常数据输出,没有边界检测(非常有可能产生堆溢出)。
8 t4 ]6 c4 q" R3 a( n' P; D比如这种代码:- 3 o# s3 q- {1 i8 l9 c( R* @! y
- char string[]="Hello World!";5 u4 [% a) d7 I! K* Z
- printf("String: %s ,强行再读一次: %#p\n", string);
8 h( c0 \$ n: B6 p& p* v" } - printf("String: %s ,强行再读一次: %#s\n", string);
复制代码 * F$ \% G! p# p3 X a- g: m7 n+ g
输出如下:3 p( Q; h R( p8 t. H# K+ }
- " O2 U& J8 ]1 }
- String:Hello World! , 强行再读一次: 0X001C1073
5 z* v5 f- G2 @4 w4 R - String: Hello World! ,强行再读一次: 閮
复制代码
& H! L1 ~- ^! G嵌入式专栏 4 问题解释问题1:printf("%f\n",a) 输出为什么是0.000000? 答:%f 提取8字节。a仅仅有4字节,提取出来的数占了float表示法的指数部分。尾数部分为0。所以终于是0& w- ]2 `0 T) \! ]
问题2:printf("%d\n",x) 输出为什么是0?答:%d 提取4字节,x有8字节。提取出来的数实际上是float表示法的指数部分(恰好是0),所以终于是0
$ Q' [+ _. Q. i! `. B' W/ l% k( l问题3:printf("%f,%f\n",a,x); 输出都是0.000000 为什么?答:參照问题1的解释。提取了八字节后,后面的已经乱了7 Y+ M s* ~* C0 B# y3 c
问题4:printf("%f,%f\n",x,a);调换一下a,x的顺序,正常了,为什么?答:这是正常的情况而已。
, s* k1 @7 Q# p5 z
7 {4 A) w, E/ |0 }* R: Q G |