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

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

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

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

  1. , W, c& Z/ S2 m1 h; u; ]/ A7 k
  2. #include <stdio.h>3 y  A( n6 D( Z! s

  3.   c$ Z! S: r7 f0 t% j! B* [
  4. void func1()
    + I( z- a. ^( o5 l; q; b5 `
  5. {
    + k& ~6 n- |3 y2 b
  6.   int a;
    : [; q) r' }0 t: X
  7.   printf("func1:%d\n", a);1 @+ ]7 c% r. |, i8 ?& T
  8.   a = 12345;
    & d. a/ T( r7 w1 K0 B, \, x6 ]
  9. }
    1 |% r' }$ Z1 j) ^( I

  10. 2 r3 e4 a& b  x/ O) E" l
  11. void func2()
    " R1 O( B# M' ~4 a3 v7 c
  12. {4 Y. [! `- ^/ K0 K; F4 R8 x
  13.   int b;
    5 h% s3 \* Z4 e7 ~/ F1 R: F& k. K/ [) }
  14.   printf("func2:%d\n", b);
    . T( D3 k* Q: e) Z$ q+ S1 O
  15. }
    % @5 v& n) W. _. k' }* o
  16. ) v- }6 |9 F' o' [
  17. void func4(). C- }9 r8 h$ X; r, F# @
  18. {9 J4 |% Y3 V# S( w  C- v# w( H
  19.   int d;4 A  s7 D" E: ^! j7 C5 @, \
  20.   printf("func3:%d\n", d);
    # e- N/ x5 g# T! v& c2 L
  21. }$ P) a# p$ X) [% V/ f
  22. : }! U: ]( N. U8 F
  23. void func3()
    0 B- M# m9 l) n8 }4 J3 `
  24. {
    2 k! W' U* X+ ]) p: W6 R
  25.   int c;( }! F5 s# d3 Z& u" U5 I" ?
  26.   printf("func3:%d\n", c);4 C1 \* Z2 R  y, I: ^+ ]6 C1 U
  27.   c = 54321;
    * e6 R2 E0 f4 r3 x3 [" J/ k( J
  28.   func4();9 s5 E/ S, R/ m* c, A$ h
  29. }1 ]( ?- l* x2 J; K! p

  30. . H! E, a5 Q' H; p) i
  31. void test_call()+ B9 Y2 Q! F' p& M* ~' X
  32. {+ e- \8 e/ S! F4 i: f9 I
  33.   func3();% c! {2 l: E! X0 j1 X$ K
  34. }
    ! b: D% {% {8 i

  35. + T4 W# ~' m$ I- }9 o( y
  36. int main(int argc, char **argv)1 w, U/ [1 n5 k8 K( x" g
  37. {# m5 Z% q  ]( D3 [7 [8 v0 F& k
  38.   func1();
    # H4 a) B+ A# ^  F4 v* C
  39.   func2();) X0 \, J6 p) z, ~/ }# f
  40. 2 {  J1 h0 j( o/ ~& W
  41.   test_call();  ]0 N# h* W+ f5 c
  42. }
复制代码
我们有func1~func4一共4个函数,其内部均有一个未初始化的局部变量,它们的值到底是多少呢?
对于这种局部变量,它们的值取决于:
  • 变量在栈中的位置。
  • 变量对应的栈位置在 之前 有没有被store过。

    ( y: @5 D* u) _
可以看到,上述第一点标记了一个内存位置,第二点则是代码的行为,也就是说,只要有代码去store对应的位置, 且后续的代码没有reset该位置的值的话,该位置就会保留着原先被store后的值。
验证非常简单,试一下就知道了:

  1. $ Z1 h1 k+ A$ s7 p0 {% Y7 m
  2. [root@localhost test]# ./a.out* R  Z- V% S& J: H( d
  3. func1:0) L( d! A5 A! P8 {
  4. func2:12345/ s) k5 j& o" J8 a
  5. func3:0
    7 k$ w* ~1 u0 x+ S. d6 E5 T) N
  6. func3:0
复制代码

8 b9 }9 G0 v6 F- T
  • 按照函数调用栈帧的变化,func1的局部变量a和func2的局部变量b显然是位于同一个位置的,在func1被调用时,这是一块新的内存(可能在进入main之前有栈帧到达过这个位置),a的值取决于调入内存该位置的页面对应偏移的初始值,这取决于操作系统:

  • 4 r5 i$ u  t( r8 G2 T
  • 操作系统在分配给程序页面时可能会将页面clear为零页。
    9 f# y8 c( R. \4 n: r
栈的分配不会涉及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. 0 {+ u+ t9 q' S7 a7 b+ Q
  2. // int a;& ~0 w, |0 X1 I. H: l5 h
  3. 00000000004005ad <func1>:
    % G  f4 V4 w+ _- t4 w$ f
  4.   4005ad:   55                      push   %rbp2 @3 I6 h( U5 _
  5.   4005ae:   48 89 e5                mov    %rsp,%rbp6 L8 O0 x8 w1 s3 {6 E
  6.   4005b1:   48 83 ec 10             sub    $0x10,%rsp
    6 {/ E' o, `8 C' f. ^
  7.   4005b5:   8b 45 fc                mov    -0x4(%rbp),%eax! R7 @% _9 U% F9 e" Z' l
  8.   4005b8:   89 c6                   mov    %eax,%esi3 K. t& V/ o  ~3 W- _0 R6 H
  9.   4005ba:   bf 90 07 40 00          mov    $0x400790,%edi' q) Z; _: L/ X: F- ^! D
  10.   4005bf:   b8 00 00 00 00          mov    $0x0,%eax* T2 D$ {5 J) y( p' L+ U$ A
  11.   4005c4:   e8 b7 fe ff ff          callq  400480 <printf@plt>
    $ F- @3 w  B! ?+ t+ W
  12.   4005c9:   c7 45 fc 39 30 00 00    movl   $0x3039,-0x4(%rbp)6 ^6 v: p) }2 w$ j
  13.   4005d0:   c9                      leaveq
    ' Z7 A5 K% Q2 ~' W! P$ Q
  14.   4005d1:   c3                      retq
复制代码
* M) g  {$ l4 T: m2 _+ i  T
  • 再看初始化局部变量a为2222的版本:

    1. # E, X4 R9 M$ B: p0 U' l  @, r4 E
    2. // int a = 2222;& g5 q& o2 P4 ]5 k  z) W/ M8 _4 Z0 X) B
    3. 00000000004005ad <func1>:
      * Q+ R$ D, u6 J5 ?! h6 J
    4.   4005ad:   55                      push   %rbp
      ( T* D) ?0 d1 O
    5.   4005ae:   48 89 e5                mov    %rsp,%rbp
      1 D( \# T- ^" t4 W* v. v
    6.   4005b1:   48 83 ec 10             sub    $0x10,%rsp0 T( E/ Y, @. T( ~. G
    7.   4005b5:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
      # w  F! V) Z* W+ ^7 d
    8.   4005bc:   8b 45 fc                mov    -0x4(%rbp),%eax5 O1 k% g3 ]+ a  Q1 L5 w* z  P1 M
    9.   4005bf:   89 c6                   mov    %eax,%esi6 Q. e% f* u6 W* ^6 G( N$ V5 _
    10.   4005c1:   bf 90 07 40 00          mov    $0x400790,%edi
      - L& |( ]- ?* ^. D
    11.   4005c6:   b8 00 00 00 00          mov    $0x0,%eax
      - o3 v7 |. K: a! l" m
    12.   4005cb:   e8 b0 fe ff ff          callq  400480 <printf@plt>
      - T+ b6 E$ u4 _2 u! K) U. K; y
    13.   4005d0:   c7 45 fc 39 30 00 00    movl   $0x3039,-0x4(%rbp)
      . Z* d9 ^/ w7 c) z) V4 p) T: p
    14.   4005d7:   c9                      leaveq
      9 Y6 S# L7 m$ N5 g: S& W5 \& G
    15.   4005d8:   c3                      retq
    复制代码
  • 仅仅差了一条指令:

    1. + |1 q" h" P" j
    2. 4005b5:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
    复制代码

    ! H8 @$ S" q9 @3 ^9 w" C
- Q7 B6 D; r! a3 W; _0 O
初始化的操作是依靠实实在在的指令完成的。
总结一句, 函数返回在pop出当前栈帧的时候,并不会清理它遗留在栈帧里的数据,下个函数调用再次重用到该栈帧的内存时,未初始化的局部变量将会被遗留数据影响,从而变得不确定!
所以,记得初始化你的局部变量。

& Z* M+ p6 e3 M# g: T2 o
赞 收藏 评论0 发布时间:2020-8-31 23:29

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版