程序员都知道,也都会使用printf函数,但你知道它也有“安全隐患”吗?
6 `* r1 ~3 E( V6 C8 I( u
下面就来举例我说说:
1 K* l: w1 r. I0 A) w7 Z6 Q+ {" I
嵌入式专栏 1 问题描述打印输出的数据并不是理论值,如下图(右边):
4 t4 C0 v$ x7 T: U- k 6 u' L' j! [1 Z! ]; l
嵌入式专栏 2 进一步描述问题请细致注意看下面代码,有如以下奇怪的现象: - 0 b7 F' b; B5 N
- int a=5;8 H4 \5 n6 l; |0 Y" n( G: I+ `- N
- floatx=a; //这里转换是没有问题的。%f打印x是 5.0000005 A, m* ~* U$ T% Y2 O7 d; D5 S: N
- 4 j* T. X( s$ ?0 k9 m: _
- printf("%d\n",a);
9 G! M* ~& _' ^6 f! I! ^ - printf("%f\n",a); //输出为什么是0.000000?-----问题1$ Y6 W& z3 w. c0 c4 ~( w. b1 i9 X
- printf("%f\n",x);* A2 {. C( X" i* `7 O
- printf("%d\n",x); //输出为什么是0?-----问题2$ I- }3 P4 _& C% }- E" j* z
- printf("%f,%f\n",a,x); //输出都是0.000000 为什么? ----问题3
2 w1 y3 Q- x8 Y9 j7 |! w - printf("%f,%f\n",x,a); //调换一下a,x的顺序,正常了,为什么?----问题40 r8 T% J; S" X6 u; O' W$ V
- printf("%d,%f\n",a,x);" ?0 G. Y3 H h, x; T$ T* F
- ; G: _8 w( K& Q* o! F+ ~$ L/ @
- getchar();9 o, J9 E/ |; [3 Q4 R" d+ F( b
- return0;
复制代码 + i# _( t% G& M/ e# T" L! \7 d
这里有四个问题,下面会进行解答。
! \4 P X8 x# j; h0 x/ l$ ~
. y: R6 s1 X, o X$ b4 x嵌入式专栏 3 printf()函数的原理解释明确这些问题首先须要明确printf()函数的工作原理。
# D: r9 n i0 @2 ]7 b& u, r8 `3 m3 bprintf()维持了一个需要打印的变量栈,默认情况下,參数进栈的顺序是由右向左的。因此,參数进栈以后的内存模型例如以下图所看到的:
Y r0 c, {: O( |& b- B9 ~5 v
打印的时候,printf依照字符转换说明符规定的格式从低地址開始提取数据。直到參数打印完。1 T% q8 d: `5 n7 W! R3 ?
比方遇到 %f 说明符就提取8个字节的数据,遇到 %d 就提取4个字节。printf()事实上不知道參数的个数,它仅仅会依据format中的打印格式的数目依次打印堆栈中參数format后面地址的内容。# L8 o& e4 U. @3 m
这样一来,printf()事实上存在安全隐患:它会强行读取内存的数据当作正常数据输出,没有边界检测(非常有可能产生堆溢出)。
( C7 t" N8 a8 E/ r4 F+ x$ v3 o: u& w比如这种代码:- 1 f* p- A3 c2 G5 P/ t, i- B
- char string[]="Hello World!";
9 v4 O9 ?/ Z% n& c - printf("String: %s ,强行再读一次: %#p\n", string);
; \! ]* X" {( D2 v% y1 i, }9 w - printf("String: %s ,强行再读一次: %#s\n", string);
复制代码 J$ C1 Y+ b- U4 t
输出如下:# T b7 Z% c' E5 {
7 k5 k1 [) _9 a3 B- String:Hello World! , 强行再读一次: 0X001C1073 " J) L( n- y' O) C- W# ]
- String: Hello World! ,强行再读一次: 閮
复制代码 2 V9 h' U% A$ B& i9 t6 M
嵌入式专栏 4 问题解释问题1:printf("%f\n",a) 输出为什么是0.000000? 答:%f 提取8字节。a仅仅有4字节,提取出来的数占了float表示法的指数部分。尾数部分为0。所以终于是00 X1 V7 q" T/ r3 g. K- l6 P
问题2:printf("%d\n",x) 输出为什么是0?答:%d 提取4字节,x有8字节。提取出来的数实际上是float表示法的指数部分(恰好是0),所以终于是0
4 J! ]0 f: k" X8 t6 U6 |3 ?问题3:printf("%f,%f\n",a,x); 输出都是0.000000 为什么?答:參照问题1的解释。提取了八字节后,后面的已经乱了
& \- ?0 e4 E- F9 B问题4:printf("%f,%f\n",x,a);调换一下a,x的顺序,正常了,为什么?答:这是正常的情况而已。
+ V1 d; F x \& I9 l# q. r! p- x8 j9 _+ t3 b8 B5 j
|