你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言中,未初始化的局部变量到底是多少?

[复制链接]
gaosmile 发布时间:2020-8-31 23:29
C语言中,未初始化的局部变量到底是多少?
答案往往是:
  • 与编译器有关。
  • 可能但不保证初始化为0。
  • 未确定。

    + a( u# U- u( e$ d% r
总之,全部都是些一本正经的形而上答案,这很令人讨厌。
但凡一些人给你滔滔不绝地扯编译器,C库,处理器体系结构却给不出一个实际场景复现问题的时候,这人大概率在扯淡。
其实,这个问题本身就是错误的问法,说全了能讲10万字,我们只要能在特定场景下确定其特定行为就OK了,当然,这就需要设计一个比较OK的实验。
在演示一个实际代码行为之前,先给出一个知识,CPU不认识变量,更无法识别变量的名字,CPU只会从特定的内存位置取值或者将值存到特定的内存位置,因此当问一个变量的值是多少的时候,必须要知道这个变量对应的值被保存在什么地方。
来看下面的代码:

  1. 2 ~% h/ f: a0 D$ d/ [9 g
  2. #include <stdio.h>
    ! T4 P0 @5 ^7 s# g' y+ r) e5 `( p/ U
  3. ) ?; X( }  }$ j- ~' I
  4. void func1()
    0 P$ T3 Q0 s- N6 e; S# h
  5. {
    3 B" W7 P: V7 F# H& c  R5 @
  6.   int a;
    5 [" X, Q+ f# P5 o* S# G" Z
  7.   printf("func1:%d\n", a);  e# ]- A+ P$ g4 d) ^) i
  8.   a = 12345;$ @% {- {) n6 ]6 R( a5 b, o
  9. }
    $ |. b/ d( Z3 f9 m% m) x, q
  10. " D/ M! P; _4 @) @  U, J
  11. void func2(). ?' J2 c& V2 y) A" j
  12. {) e7 w6 D/ N, I9 [! L5 `" x
  13.   int b;
    ( H( ?) `% B* M) Y' |8 P
  14.   printf("func2:%d\n", b);% c" U. c0 y+ W+ n/ A0 b) X
  15. }- d2 d* c7 q/ R8 h- j( x
  16. 1 K: n. ~( s4 s$ l
  17. void func4()+ M+ w3 c+ ^, o( S8 d
  18. {
    2 [4 V0 s1 P6 @$ `, @
  19.   int d;
    ; q* d& R$ e& H: s+ m7 d6 y5 e
  20.   printf("func3:%d\n", d);
    2 v+ M/ A# s8 q. }
  21. }- b% Q( T6 C. v+ k: a7 |, r* P; {

  22. * o/ G6 {+ G9 U6 f" \
  23. void func3()
    , F0 U" @# `" {) h6 d4 O( N
  24. {
    . w8 W! M3 h; R" `' ]
  25.   int c;
    2 l) y4 @6 T7 O, J. h
  26.   printf("func3:%d\n", c);
    " B4 O! E2 x0 i; n/ K' w
  27.   c = 54321;
    8 P4 g2 q0 ?3 W9 W5 V, x# f. A
  28.   func4();
    ; c: E+ r  T6 R, ~( z5 V
  29. }
    3 G4 J9 A( u; O- P
  30. 6 ^7 \2 P# O* R" o( g# S: v' B
  31. void test_call()5 q) |1 ~! F6 G$ h
  32. {: H# H* i* c' g+ k$ b
  33.   func3();. k% S) N$ `' y# W+ |: Y: }" k; O! l
  34. }
    ! r1 M3 }& R0 X+ J0 M1 @
  35. 5 Y. {* B# H. g' e
  36. int main(int argc, char **argv)$ k* F  p+ B0 E; z+ M7 |6 g/ q
  37. {
    5 Z2 g1 K; B1 E: p8 J
  38.   func1();
    $ ]0 c9 d0 l: O% Y' j. i6 _9 k
  39.   func2();
    ) B" Q( m! W* v# q0 f

  40. 1 p+ D& `% h8 L+ U7 ^- u
  41.   test_call();
    . x- p: J; j# p
  42. }
复制代码
我们有func1~func4一共4个函数,其内部均有一个未初始化的局部变量,它们的值到底是多少呢?
对于这种局部变量,它们的值取决于:
  • 变量在栈中的位置。
  • 变量对应的栈位置在 之前 有没有被store过。

    ! ?' B1 |8 S- E! q
可以看到,上述第一点标记了一个内存位置,第二点则是代码的行为,也就是说,只要有代码去store对应的位置, 且后续的代码没有reset该位置的值的话,该位置就会保留着原先被store后的值。
验证非常简单,试一下就知道了:
  1. 9 E: g: T, b) O  g
  2. [root@localhost test]# ./a.out
    : ]! o$ g# x4 a; ?+ o* I5 m3 J
  3. func1:0
    0 e2 ~$ O% g4 m. N1 T( A
  4. func2:12345
    : W1 p3 I; ?. w1 O* W' c
  5. func3:0# ?: @  C* G& Y0 P7 i
  6. func3:0
复制代码
- g: W. {: p# b" c6 [
  • 按照函数调用栈帧的变化,func1的局部变量a和func2的局部变量b显然是位于同一个位置的,在func1被调用时,这是一块新的内存(可能在进入main之前有栈帧到达过这个位置),a的值取决于调入内存该位置的页面对应偏移的初始值,这取决于操作系统:

  • 9 E1 R/ \: H% a! x+ u
  • 操作系统在分配给程序页面时可能会将页面clear为零页。
    , ~% q- m* p5 c7 g# ?7 G3 V
栈的分配不会涉及C库,这里显然并不涉及C库的行为,但类似malloc分配的内存则涉及C库了。
打印结果,a的值为0,我们认为操作系统返回给了应用程序零页。接下来在func1中将其赋值12345之后函数返回,接下来调用func2的时候,在之前func1已经退出的栈帧位置重建栈帧,对应位置依然还是12345。
我没有看到func1的ret操作后面有stack清0的代码指令。效率考虑,也不该有这样的指令。
再看test_call函数,很明显,func3和func4调用使用的并不是同一个栈帧,因此即便是在func3中对c赋值了54321,也不会影响在其栈帧之上的func4的栈帧对应位置的值d。因此c和d的初始值均保持为0。
那么,初始化一个局部变量和不初始化一个局部变量,在指令层面上,区别在哪里呢?
很简单,亲眼看一下就知道,先看未初始化局部变量的func1:

  1. ) K9 Y# g! j" n, g  r& }
  2. // int a;' I$ T+ Q% |1 {3 I8 R- V6 L
  3. 00000000004005ad <func1>:' x, O/ a2 j3 x
  4.   4005ad:   55                      push   %rbp
    % q, c" }/ C+ `, d2 E& Y* x
  5.   4005ae:   48 89 e5                mov    %rsp,%rbp
    & h5 V* |. o: \9 r) K9 ^
  6.   4005b1:   48 83 ec 10             sub    $0x10,%rsp0 i. u# X$ W' k7 j& A4 {& m4 r8 d
  7.   4005b5:   8b 45 fc                mov    -0x4(%rbp),%eax
    3 v, b5 v0 m8 e
  8.   4005b8:   89 c6                   mov    %eax,%esi
    ; O( p7 F# W& [+ R5 D
  9.   4005ba:   bf 90 07 40 00          mov    $0x400790,%edi* L2 y1 [7 t0 V
  10.   4005bf:   b8 00 00 00 00          mov    $0x0,%eax& F+ R8 P% d+ }/ z
  11.   4005c4:   e8 b7 fe ff ff          callq  400480 <printf@plt>
    5 q) K5 T( d- K* b
  12.   4005c9:   c7 45 fc 39 30 00 00    movl   $0x3039,-0x4(%rbp)
    % A6 j% o/ d3 {# g; u
  13.   4005d0:   c9                      leaveq% I+ _+ G0 @0 i9 [/ d' r
  14.   4005d1:   c3                      retq
复制代码
! s$ A$ Z: A9 W8 W9 I6 D5 s
  • 再看初始化局部变量a为2222的版本:

    1. , p$ L* N. K& @/ x8 p% `6 E
    2. // int a = 2222;! {: s& u$ W2 u  i" u) q0 ^8 G
    3. 00000000004005ad <func1>:
      # e) w; F! p  z8 w
    4.   4005ad:   55                      push   %rbp
      4 g% ?3 @  F8 r) B
    5.   4005ae:   48 89 e5                mov    %rsp,%rbp( t9 N+ `8 O! ^: T3 a
    6.   4005b1:   48 83 ec 10             sub    $0x10,%rsp! J' _, f& g% x6 s3 K7 K7 W4 G$ J8 j; }
    7.   4005b5:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
      2 g" \( V3 {$ u: v$ k- P5 x
    8.   4005bc:   8b 45 fc                mov    -0x4(%rbp),%eax
      : A9 @& u1 ]. j( V% H% I. d, |: {
    9.   4005bf:   89 c6                   mov    %eax,%esi$ O; J4 w  f( y8 [
    10.   4005c1:   bf 90 07 40 00          mov    $0x400790,%edi
      7 N/ o9 b" _8 h0 N3 a
    11.   4005c6:   b8 00 00 00 00          mov    $0x0,%eax* G: H2 x: H0 n3 W, k
    12.   4005cb:   e8 b0 fe ff ff          callq  400480 <printf@plt>5 Z/ u  `0 o  U# O5 G" }
    13.   4005d0:   c7 45 fc 39 30 00 00    movl   $0x3039,-0x4(%rbp)
      3 T* o) H& [% g8 x
    14.   4005d7:   c9                      leaveq4 t: k$ k. j- @$ ^/ \6 A( A8 I
    15.   4005d8:   c3                      retq
    复制代码
  • 仅仅差了一条指令:

    1. 8 ~7 r+ B1 M8 U2 m8 a5 [
    2. 4005b5:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
    复制代码
    7 `6 V( d3 P1 u. x
: G9 m5 y. I9 t% ?4 n' K
初始化的操作是依靠实实在在的指令完成的。
总结一句, 函数返回在pop出当前栈帧的时候,并不会清理它遗留在栈帧里的数据,下个函数调用再次重用到该栈帧的内存时,未初始化的局部变量将会被遗留数据影响,从而变得不确定!
所以,记得初始化你的局部变量。

- F( r/ M4 @1 W# R1 D
收藏 评论0 发布时间:2020-8-31 23:29

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版