程序员都知道,也都会使用printf函数,但你知道它也有“安全隐患”吗? & @. x1 L4 }& u% x- S X
下面就来举例我说说:
" J4 [1 a8 x4 Y
嵌入式专栏 1 问题描述打印输出的数据并不是理论值,如下图(右边):% n4 x: S; U: }' b! `2 s8 j0 R
; u. \' L' K7 |7 m6 J
嵌入式专栏 2 进一步描述问题请细致注意看下面代码,有如以下奇怪的现象: - t( ?/ Q1 }. H- T# B. N
- int a=5;3 }7 X2 ]) b. T2 }: C1 z
- floatx=a; //这里转换是没有问题的。%f打印x是 5.000000
9 T' G4 P# p c! x: G u. T6 k
* m; w" b- b" I, o7 |' @4 \- printf("%d\n",a);- I! ], p$ |* l7 Q: v: @- k, ?3 y
- printf("%f\n",a); //输出为什么是0.000000?-----问题1
5 o7 G6 D5 C& p* N: z0 l6 f2 i - printf("%f\n",x);! N" G: ?% w$ R) t$ O* `- P# O
- printf("%d\n",x); //输出为什么是0?-----问题2- J0 e" [( I& b$ k# a
- printf("%f,%f\n",a,x); //输出都是0.000000 为什么? ----问题3( f+ r3 {+ `4 h, Y4 d4 M( _
- printf("%f,%f\n",x,a); //调换一下a,x的顺序,正常了,为什么?----问题4% Q. i3 w/ x( U7 H* [4 \
- printf("%d,%f\n",a,x);
" x5 _ m2 Z% i# S+ Y - & L4 [$ ?1 V+ Z! x; e. Y
- getchar();- c1 b% F# ]8 z1 n- K9 y" y
- return0;
复制代码 8 a8 u/ \9 ~+ |8 R* D
这里有四个问题,下面会进行解答。
' Z% t; l' B7 h
% t8 e( f: t) v6 i0 a7 o9 b, r+ F嵌入式专栏 3 printf()函数的原理解释明确这些问题首先须要明确printf()函数的工作原理。
, a: R: i" d) s: ~( H$ lprintf()维持了一个需要打印的变量栈,默认情况下,參数进栈的顺序是由右向左的。因此,參数进栈以后的内存模型例如以下图所看到的:
. o3 Y! b3 _% ?" Q+ @
打印的时候,printf依照字符转换说明符规定的格式从低地址開始提取数据。直到參数打印完。
& t! A- H; g0 o4 t$ G8 [6 t比方遇到 %f 说明符就提取8个字节的数据,遇到 %d 就提取4个字节。printf()事实上不知道參数的个数,它仅仅会依据format中的打印格式的数目依次打印堆栈中參数format后面地址的内容。' N4 \2 n3 N3 k8 g/ [' d
这样一来,printf()事实上存在安全隐患:它会强行读取内存的数据当作正常数据输出,没有边界检测(非常有可能产生堆溢出)。( ~- R& c* E6 ]$ M
比如这种代码:- 1 q+ Y, N. _7 @$ ~- A
- char string[]="Hello World!";
% E" q5 d! F5 Z M7 T - printf("String: %s ,强行再读一次: %#p\n", string);
+ N& M1 \& E& F$ P- n/ w) V. u - printf("String: %s ,强行再读一次: %#s\n", string);
复制代码
5 u/ V, a8 U8 F3 H! s3 ?3 |1 v输出如下:
% B: [0 G+ N, a2 F4 |1 j6 n
9 ?' |' y# e/ t% y: K5 H4 S8 E) j) g- String:Hello World! , 强行再读一次: 0X001C1073 # Q1 ?) V6 |2 `; P, f
- String: Hello World! ,强行再读一次: 閮
复制代码 ; k) t) _5 ^8 W( R" |
嵌入式专栏 4 问题解释问题1:printf("%f\n",a) 输出为什么是0.000000? 答:%f 提取8字节。a仅仅有4字节,提取出来的数占了float表示法的指数部分。尾数部分为0。所以终于是0
3 F: f$ c% N1 S# y& \# o问题2:printf("%d\n",x) 输出为什么是0?答:%d 提取4字节,x有8字节。提取出来的数实际上是float表示法的指数部分(恰好是0),所以终于是0
8 {: R3 Z; a' R! Q2 D问题3:printf("%f,%f\n",a,x); 输出都是0.000000 为什么?答:參照问题1的解释。提取了八字节后,后面的已经乱了
( Z* v% g3 q$ r2 j6 H$ U问题4:printf("%f,%f\n",x,a);调换一下a,x的顺序,正常了,为什么?答:这是正常的情况而已。
& O4 v3 h( {. u5 _* d8 g; v# d0 Z4 l
+ [2 L R) E7 y: F# W |