关于静态变量“赋值无效问题”的探讨结果
来源:互联网 发布: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愚见,望给位高手批评讨论什么地方说的不好的。
- 关于静态变量“赋值无效问题”的探讨结果
- 关于静态成员变量的赋值
- 关于“ora-01483:DATE或NUMBER赋值变量的长度无效”的问题
- 关于final变量的赋值问题
- 变量的初始化与赋值操作无效
- 关于静态变量编译的问题
- 关于静态变量的内存分配问题
- Java静态变量加载赋值问题
- 关于对象初始化和赋值的探讨
- c# winform 关于给静态全局变量赋值的问题
- 关于为c++类中的变量赋值的问题
- 关于React Native 网络请求以及变量赋值的问题
- 关于C#中readonly的变量赋值问题。
- shell 为变量赋值 命令的输出结果赋值
- 关于类静态成员变量指针通过动态分配的内存如何回收的探讨
- 关于静态变量初始化问题
- 关于JAVA继承类的静态变量、成员变量、父子类构造方法调用顺序的探讨
- 关于c语言中变量赋值问题
- 用HTML5绘制骰子
- oracle 11g rac 安装报错 CRS-2500
- [leetcode刷题系列]Rotate Image
- 在Eclipse中跑b3log的设置
- Problem 11:Largest product in a grid
- 关于静态变量“赋值无效问题”的探讨结果
- Google-ProtoBuf数据编码
- 【原来如此】为什么很多应用都安装在/usr/local目录下?
- 菜鸟学Java(八)——dom4j详解之读取XML文件
- HDU--杭电--1240--Asteroids!--广搜--贴个水题别群殴我
- springside 备注
- 解决Win7 64位安装 Microsoft .NET Framework 4 失败的情况
- Ubuntu下用C语言访问MySQL数据库
- 数据库分库分表介绍