关于静态变量“赋值无效问题”的探讨结果

来源:互联网 发布:nagle算法作用 编辑:程序博客网 时间:2024/05/18 02:48

吐槽一下百度空间就是渣,换空间鸟。。。。

今天遇到一个关于静态变量“赋值”,值不变的问题。顿时觉得不可理解,很是奇怪

关于静态变量问题的探讨

 

 

在这里是static int num = 1 在第一次赋值之后,貌似num = 1 这句话就没了作用,让人觉得“很奇怪”,虽然大家都知道,static 储存在静态内存里面,但是怎么就不管那个赋值语句了呢?

 

首先,要搞清楚这个问题,要知道什么叫初始化,其实学了很久,好多人还是不知道什么叫初始化,声明,定义。

我们来看 《ISO IEC 9899 1999 (C99)》标准里面怎么说的,我想不要我科普这家伙是绝对权威吧!O(∩_∩)O哈哈~

什么书都得靠边站O(∩_∩)O哈哈~

C 99中如是说:

A declaration specifies the interpretation and attributes of a set of identifiers.

A definition of an identifier is a declaration for that identifier that:
— for an object, causes storage to be reserved for that object;
— for a function, includes the function body;
98)
— for an enumeration constant or typedef name, is the (only) declaration of the
identifier

声明是个神马东西,

A declaration specifies the interpretation and attributes of a set of identifiers.

声明是对标示符的解释和属性的详述

 

变量的定义是就是一种声明!只不过它是一种特殊的声明罢了,特殊就在

for an object, causes storage to be reserved for that object;

第一“定义变量”,
对于一个变量或说对象(object),为该变量分配内存

 

 

//----------------------------------------------------------和讨论主题没多大关系可以跳过-------------------------------------------------------------------------- 

第二“定义函数”,对于一个函数,定义是指包含这个函数功能完全的代码块,就是要包含整个函数整体,includes the function body;
第三,“枚举类型或typedef类型定义”,定义就是对表示符(identifier)唯一的声明

//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 

 

上面啰啰嗦嗦说了很多,无非很强调的说明有一个概念——定义是一种特殊的声明

然后,有点逻辑的话都知道,定义只有一次,而声明可以多次(同一个标识符,标识的地址不可能有两个啦。。。)

然后我们可以这样说,变量的定义是出现变量的第一次说明!(想想看是不是)

很多人都喜欢说

int  a;是变量a的定义,但是人们忽略了一种情况,这句话是有前提的,单文件编译!如果你是多文件编译呢?

没图没真相╮(╯▽╰)╭,上图!

我自己想出来的一个例子,放心啦,零错误零警告。。。运行过了的

然后我们进入正题!

都是int a;你说int a是定义?有谁敢说

 

所以,int  a;是变量a的定义这句话如果是对的,前提条件是,单文件编译,当多文件编译的时候int  a就可能是声明了(注意此处我说用声明指不是定义的那一种约定俗成的声明)

比较好的风格的写法是extern int  a;

 

而且如果两处都是 int a =10;的话,恭喜,可以通过编译!但是运行不了!其实就是编译器故意限制的避免重定义,不允许在声明中采用赋值这种操作

连extern int a =10;这里都不行!声明是不能赋值的。

 

所以我们知道,初始化这个东西只能在定义的时候做,换句话说,只有定义时候的赋值操作叫做“初始化”,其他时候的赋值操作只能叫做“赋值”

 

,初始化,是伴随着定义而发生的!

 

其实吧,人们把可以的把声明和定义区分开来也是用意的,区别那个分配内存的问题,所以人们常说的“声明”就默认不是“定义”了

 

说完了定义,说完了声明,咱说初始化;

 

int a ;这是定义(前提条件刚说的别忘了)

int a =字面常量; 这种也是定义(这绝对是定义,否则发生重定义)

无论是什么变量,初始化只发生在定义的时候,之后像

int a;//这个家伙是如果第一次出现,是定义,开辟寻找一块内存空间,然后把变量a标记在那块地址上

a = 1;把字面量1的值存储到变量a标记的那块地址中去,这种叫赋值;

 

我对初始化,定义,声明的这种理解在C++这种更规范化的语言的标准里得到了印证

A declaration is adefinition unless it declares a function without specifying the function’s body (8.4), it
contains the extern specifier (7.1.1) or alinkage-specification(7.5) and neither aninitializer nor afunction-body, it declares a static data member in a class definition (9.2, 9.4), it is a class name declaration (9.1), it is anopaque-enum-declaration(7.2), it is atemplate-parameter(14.1), it is aparameter-declaration(8.3.5) in a
function declarator that is not thedeclarator of afunction-definition, or it is a typedefdeclaration (7.1.3),
analias-declaration(7.1.3), a using-declaration(7.3.3), a static_assert-declaration(Clause 7), anattribute-declaration(Clause 7), anempty-declaration(Clause 7), or ausing-directive(7.3.4)

 

Example:all but one of the following are   definitions:
       int a; // definesa   //这里就很好的处理了C允许缺省extern 来声明的问题,C++更本就不允许你那样声明,具体语言的细节处理就比C要好,所以C++自然就比C规范很多
      extern const int c = 1; // definesc
      int f(int x) { return x+a; } // definesfand definesx
      struct S { int a; int b; }; // definesS, S::a, and S::b
      struct X{//  definesX
       int x; // definesnon-static data memberx
      static int y; // declares static data member y
        X(): x(0) { } // defines a constructor of X
     };


           int X::y = 1; // definesX::y
          enum { up, down }; // definesupanddown
       namespace N { int d; } // definesNandN::d
      namespace N1 = N; // definesN1
        X anX; // definesanX
     

whereas these are just declarations:
        extern int a; // declaresa
       extern const int c; // declaresc
        int f(int); // declaresf
        struct S; // declaresS
        typedef int Int; // declaresInt
       extern X anotherX; // declaresanotherX
        using N::d; // declaresd
— end example]

我就不翻译了,有兴趣的可以看看

.然后我们说完初始化,说完定义,说完声明,貌似跑题了,其实不跑题

 

static int num = 1;

 

“静态变量只初始化一次,之后不对其初始化”这句话就很关键了!也就是说,编译器在遇到第一次static int num = 1之后,不再对其初始化,也就是说,定义时候的赋值是不会再发生了!就是这里不会有第二次第三次第四次赋值为1的情况发生了。可以理解为编译器会跳过这个语句

 

所以调用四次fun函数的结果自然就是, 5 4 3 2(对于代码一中static变 量值)

反汇编同样可以说明这一点

首先静态变量标识符对地址的标记是不会改变的,所以num作为左值的时候始终标记那块内存,然后把num地址里面的值赋给eax寄存器(00BE145E  mov         eax,dword ptr [num (0BE7000h)] )

接着把寄存器的值加1(00BE1463  add         eax,1 ),

然后把eax寄存器的值赋给num地址标记的内存块(00BE1466  mov         dword ptr [num (0BE7000h)],eax)

最后把num地址中的值赋给eax寄存器(00BE146B  mov         eax,dword ptr [num (0BE7000h)] )

这就是整个fun函数反汇编出来的结果,然后eax寄存器里面储存的值也就是num已经+1之后的值供printf打印使用

 

有兴趣的话,可以一步一步的调试反汇编,观察寄存器值的变化,这里我自己玩了一下,确实看见eax寄存器的值是取num地址值,然后add加一的

 

总结一下这个问题的要点

1:初始化只发生在变量定义的时候

2:静态变量只在程序运行开始时初始化一次,之后不对其初始化

 

这样就解释了为什么会有“无视”赋值语句的现象,确实是无视O(∩_∩)O哈哈~然后,后来我和别人交流的时候,我听别的高手说,这种用法在数据结构的用的很多,反复调用静态变量值,并修改该值。这句话也反映了我的水平有限,知道的太少,还要和各位一同努力,哇咔咔

 最后给个教训,有语法问题的时候就别翻什么教材(当然我不看教材的),也不要翻大牛出的书了,直接去翻圣经——《ANSI C》或则《c99》推荐这两个标准(原因不再多做探讨,个人推荐而已),语法都是这里定义的,还有什么比这权威更具说服力

 

EOF愚见,望给位高手批评讨论什么地方说的不好的。