程序员都知道,也都会使用printf函数,但你知道它也有“安全隐患”吗?
1 U1 z! _7 T4 z. f
下面就来举例我说说:
/ V9 ~/ ^& P/ r; H+ G$ {
嵌入式专栏 1 问题描述打印输出的数据并不是理论值,如下图(右边):$ c& `2 l5 Z Q5 W
# l* A: Y& c: r2 O! g! a' z
嵌入式专栏 2 进一步描述问题请细致注意看下面代码,有如以下奇怪的现象: - ( C# m8 p) p" y. u. s& B! S& Z
- int a=5;3 Y# Q# a: X' e* Z" o9 e
- floatx=a; //这里转换是没有问题的。%f打印x是 5.000000
- e ?! E* f/ p* ?! i - 2 a1 b* w8 l: B' h0 M0 @! Z
- printf("%d\n",a);
# W# u& ~1 E) p6 v2 _; x - printf("%f\n",a); //输出为什么是0.000000?-----问题1
* H5 R' b! ~" i) \ - printf("%f\n",x);
3 n _, z) S; l, O" h+ P: f$ D" A - printf("%d\n",x); //输出为什么是0?-----问题25 @* u- w& J V# ]
- printf("%f,%f\n",a,x); //输出都是0.000000 为什么? ----问题3
) k0 V+ r, v( C" j+ h3 l# D7 Z - printf("%f,%f\n",x,a); //调换一下a,x的顺序,正常了,为什么?----问题4
3 f1 L9 L( D& v5 U6 ? - printf("%d,%f\n",a,x);
# W, c }% r- | O - 7 R' `, b9 t9 y% S: d
- getchar();
) A/ o: G4 k8 q( V* T - return0;
复制代码
( z" f5 F( @+ M) Q8 W这里有四个问题,下面会进行解答。: C2 `+ @9 v1 z/ s |/ p
5 S7 X. M$ X1 D
嵌入式专栏 3 printf()函数的原理解释明确这些问题首先须要明确printf()函数的工作原理。$ ~5 e; @8 u6 X2 ?" L! x7 t
printf()维持了一个需要打印的变量栈,默认情况下,參数进栈的顺序是由右向左的。因此,參数进栈以后的内存模型例如以下图所看到的:
D, C; Z: ] Y6 K
打印的时候,printf依照字符转换说明符规定的格式从低地址開始提取数据。直到參数打印完。% N! Y' _7 e7 j C0 h
比方遇到 %f 说明符就提取8个字节的数据,遇到 %d 就提取4个字节。printf()事实上不知道參数的个数,它仅仅会依据format中的打印格式的数目依次打印堆栈中參数format后面地址的内容。
- D7 C( U+ l0 O; S这样一来,printf()事实上存在安全隐患:它会强行读取内存的数据当作正常数据输出,没有边界检测(非常有可能产生堆溢出)。" Q: v' j8 \* c3 i c+ H
比如这种代码:- 4 ^/ q& n- _* @
- char string[]="Hello World!";+ X) }6 f f3 |8 L8 i: Q' D" L
- printf("String: %s ,强行再读一次: %#p\n", string);% R |7 z+ V3 d2 J
- printf("String: %s ,强行再读一次: %#s\n", string);
复制代码
0 K9 g e( d. J8 X5 k2 @输出如下:, {! c. p$ ]- g$ L0 B
- ' F+ p. I. v v- k% v& }
- String:Hello World! , 强行再读一次: 0X001C1073
0 n6 w+ t9 r; o- W2 ] - String: Hello World! ,强行再读一次: 閮
复制代码
& j9 P# f" V6 F5 |嵌入式专栏 4 问题解释问题1:printf("%f\n",a) 输出为什么是0.000000? 答:%f 提取8字节。a仅仅有4字节,提取出来的数占了float表示法的指数部分。尾数部分为0。所以终于是0 E9 V. |1 H" d" @- P) a8 @7 @
问题2:printf("%d\n",x) 输出为什么是0?答:%d 提取4字节,x有8字节。提取出来的数实际上是float表示法的指数部分(恰好是0),所以终于是02 m% m' n, \ O4 |1 c' n
问题3:printf("%f,%f\n",a,x); 输出都是0.000000 为什么?答:參照问题1的解释。提取了八字节后,后面的已经乱了+ H( y3 v o [5 L( d; }5 b
问题4:printf("%f,%f\n",x,a);调换一下a,x的顺序,正常了,为什么?答:这是正常的情况而已。 _, l" N- j& Y4 T% u* P
% u- z4 ]& P* q# |" t( r
|