`
fullfocus
  • 浏览: 100743 次
  • 来自: 厦门
最近访客 更多访客>>
社区版块
存档分类
最新评论

《Essential c++中文版》读书笔记--静态局部变量(一)

阅读更多

   今天碰到一个局部静态变量的作用域的问题。有些诱惑,所以回去上机实践了以下,总结如下:

cpp 代码
  1. #include <stdio.h></stdio.h> <stdio.h></stdio.h> <stdio.h>
  2. int* f1(int i)   
  3. {   
  4.   
  5.     static int s = 2;  //局部静态变量,其只在本函数之内存在。但又与局部变量相区别,其在离开函数时并不消失   
  6.     s = s +i;   
  7.     return &s;  //可以返回地址   
  8. }   
  9. void main()   
  10. {   
  11.  int *a =   f1(1);   
  12.   
  13.  printf("cout:%d\n",*a);   
  14.     
  15.  a = f1(1);    // s只初始化一次   
  16.   
  17.   printf("cout:%d\n",*a);  //   
  18.   
  19.   //s  = s+1;   出错,s在此范围不存在。其不是file scope   
  20.   
  21. }  

运行结果:

 

  • 大小: 106.7 KB
分享到:
评论
35 楼 iihero 2007-02-20  
Elminster 写道
很奇怪为什么会扯成这个样子 ……

问题的关键在于全局变量的构造次序。如果你的全局变量不是 int 而是一个类类型的话,那么它需要先构造才能正常使用,而问题就在于构造函数的调用次序是 C++ 标准没法保证而由实现定义的。比方说:

a.cpp
const string msg = "Hello, world!";

b.cpp
const string display = msg;

这里 msg 和 display 分别是位于 a.cpp 和 b.cpp 的两个全局变量。如果 display 的构造函数先于 msg 的构造函数被调用,就会出现奇怪的结果,比如说 display 变成一个空字符串什么的。这里的问题就是这个次序是 C++ 标准所不保证的,而是由编译器的实现来决定。因此如果你的代码依赖于全局变量的构造次序,你的代码就有可移植性问题。
强烈同意楼上的。
34 楼 Arath 2007-01-19  
另外这种语法在C中是不允许的.
C++允许,而且只要是符合C++标准的编译器都会合理的处理先后顺序的,除非写出
a.cpp
int gi = gj+3;

b.cpp
int gj = gi+13;
这样bt的代码,如果这样很多编译器也不报错,但是结果就不可预期了,谁让你自己写出这样矛盾的代码呢?

33 楼 Arath 2007-01-19  
Elminster 写道
很奇怪为什么会扯成这个样子 ……

问题的关键在于全局变量的构造次序。如果你的全局变量不是 int 而是一个类类型的话,那么它需要先构造才能正常使用,而问题就在于构造函数的调用次序是 C++ 标准没法保证而由实现定义的。比方说:

a.cpp
const string msg = "Hello, world!";

b.cpp
const string display = msg;

这里 msg 和 display 分别是位于 a.cpp 和 b.cpp 的两个全局变量。如果 display 的构造函数先于 msg 的构造函数被调用,就会出现奇怪的结果,比如说 display 变成一个空字符串什么的。这里的问题就是这个次序是 C++ 标准所不保证的,而是由编译器的实现来决定。因此如果你的代码依赖于全局变量的构造次序,你的代码就有可移植性问题。


说的是没有错,这就要考验编译器的能力,如果编译器分析够全面这种相关性是可以处理的,并且可以保证其正确性,但正如你所说如果编译器作的不好那么移植性将大打折扣.
我个人来说是不会使用这种代码的,宁可自己在用到的地方赋值好了,多写一点代码,保证不会出错
32 楼 Elminster 2007-01-19  
很奇怪为什么会扯成这个样子 ……

问题的关键在于全局变量的构造次序。如果你的全局变量不是 int 而是一个类类型的话,那么它需要先构造才能正常使用,而问题就在于构造函数的调用次序是 C++ 标准没法保证而由实现定义的。比方说:

a.cpp
const string msg = "Hello, world!";

b.cpp
const string display = msg;

这里 msg 和 display 分别是位于 a.cpp 和 b.cpp 的两个全局变量。如果 display 的构造函数先于 msg 的构造函数被调用,就会出现奇怪的结果,比如说 display 变成一个空字符串什么的。这里的问题就是这个次序是 C++ 标准所不保证的,而是由编译器的实现来决定。因此如果你的代码依赖于全局变量的构造次序,你的代码就有可移植性问题。
31 楼 Arath 2007-01-19  
七猫 写道

如上面的这个例子,
其中i是放在数据段,已经被初始化好了,就是1,
CA,CB的构造函数是放在好像是一个表里面的,由MainCrtStartup来执行(用_init_term来完成的),有兴趣的可以直接看C库代码。我不贴了。


构造函数和全局变量不一样喔~
你所指的构造函数在一个表里,实际上是C++的特性,不过总的来说方法就那么几种,无论变量还是函数~
30 楼 Arath 2007-01-19  
在目前大部分常用的操作系统中,可执行文件里都是按照特定文件格式放置所需要的代码和数据,例如windows的PE(COFF), linux的ELF等,所以像有初值的全局变量一般都会放在一个固定的data section, COFF里面是.data(.bss是不需要初始化的全局变量),很多操作系统的loader会支持将这些数据段在进入main之前就放置到内存中去,但这不是唯一方法.还有的方法就是有和操作系统相关的library提供代码在main前加载,有些干脆由编译器自己产生加载的代码.
所以说具体如何实现要大家商量好,当然一般情况下是OS已经决定了,编译器只能被动的去匹配而已.
C++的构造函数是在对象被实例化的时候调用的,这个和全局变量的初始化有异曲同工的作用.
对于局部静态变量的初始化则完全是在函数入口处添加代码来做到的,这一点也就是前面提到的为了保证初始化的次序.但是这种动作不会因为有这种特性而消除初始化错误的可能,因为代码逻辑一旦错误,依然得不到正确的初始化值,所以不推荐这种方法,与其惴惴不安的使用不如老老实实传参数不是更好?
不过这种做法有个好处...对于搞破解来说多了一道坎~~
29 楼 七猫 2007-01-19  
#include <stdio.h>
#include <stdlib.h>


class CA
{
public:
CA()
{
printf("Enter %s\n",__FUNCTION__);
}
~CA()
{
printf("Leave %s\n",__FUNCTION__);
}
};

class CB
{
public:
CB()
{
printf("Enter %s\n",__FUNCTION__);
}
~CB()
{
printf("Leave %s\n",__FUNCTION__);
}
};

CA gca;
CB gcb;

int i=1;

int main(int argc,char *argv[])
{
printf("We are coming...%d\n",i);
return 0;
}

如上面的这个例子,
其中i是放在数据段,已经被初始化好了,就是1,
CA,CB的构造函数是放在好像是一个表里面的,由MainCrtStartup来执行(用_init_term来完成的),有兴趣的可以直接看C库代码。我不贴了。
28 楼 七猫 2007-01-19  
你说的可能是linux中的.bss或者是pe中的.data段之类的东东吧?这些当然可以由加载器帮你搞定,其实你可以直接用ultraedit或者ida打开的,这些本来就是已经初始化好的变量在里面,加载器基本上只要把他们直接读到内存就可以了。

但我认为我们讨论的不仅仅是:
static int g_systeminit=1;这么简单的事情,我们要讨论的可能还涉及到类的构造函数,这个一般是加载器调用程序的一个入口函数,这个入口函数并不是你平时所写的main,WinMain函数,而是编译器内部的一个(WIN下面的maincrtstartup,在这里面可能会做这些全局变量的初始化的工作),我前面仅仅提到次序问题,而不是他们是否能确保的问题。
次序就是你要是两个全局变量(他们都有构造函数),这两个全局变量的初始化次序可能是无法确定的。

这是我的印像中的理解,但我也记不太清楚C++库里的全局变量的构造函数在何时会被调用,可能是入口后的那部分吧,楼上那位搞过编译器的兄弟能否给我们讲解一下编译器是怎么处理这块内容的呢?
27 楼 runes 2007-01-19  
Arath 写道
我所提出
引用
不知道为什么会提出全局变量构造难以保证,这一点必须是编译器与操作系统协调好的,否则后果不堪设想

是针对
Elminster 写道
全局变量理论上只能保证在 main 函数之前构造完毕,同时一个翻译单元内的全局变量按声明次序构造,除此之外没有任何保证

而来,你就抓住了操作系统这一点和我掰.
一般来说编译产生的最终代码中,需要初始化的数据都会被放在一个initialized data section中,这样操作系统支持的话就可以直接在进入main之前将这个section中的值来初始化对应的内存区域, 但是在某些操作系统中,loader不具备这个功能甚至没有loader,如果这样就需要编译器产生初始化全局变量的代码放在main之前执行.
正是基于这一点我才会说“编译器与操作系统协调好”.
现在明白了不?
讨论技术有争执没有什么,但是要上升到人身攻击那就只能说明自身的粗浅.

到此为止,我不会再回你任何帖子!


呵,“同时一个翻译单元内的全局变量按声明次序构造,除此之外没有任何保证”,我认为就是指的有依赖的关系的那种,要不干吗提“声明次序构造”呢?

需要初始化的数据都会被放在一个initialized data section中,和“没有依赖关系的全局变量初始化的值”之间有关系吗? 那个值编译期就决定了。

“样操作系统支持的话就可以直接在进入main之前将这个section中的值来初始化对应的内存区域”
你那“个section中的值”是不是编译期定下来的???

如果你非得强调 操作系统初始化section,我感觉就像说“每个月的薪水(公司给你定好的)是依赖于ATM机(取款方式)”。

至于你说什么人身攻击吗,随你怎么想,我感觉论坛里面说话吗,直接一些大家简单明了,观点尖锐一些很快就能把问题讨论清楚,还是那句话,智者见智,仁者见仁,当然淫者也见淫。


最后,你说“到此为止,我不会再回你任何帖子!”

呵呵,我又不需要 有人来给我抬轿子,爱回不回,你的手长在你身上,没有人强迫你,请随意!
26 楼 fullfocus 2007-01-18  
现在这个问题也越来越深入了,正因为大家的讨论,才能碰撞出火花,把问题给剖析的比较深入.

作为lz, 我很高兴大家的踊跃发言,讨论的时候有技术上的争执,思想上的差异,都是非常正常的.不过这样子,我们才会一起进步,一起克服问题. 希望我们讨论的时候,能做到对事不对人,努力构造一个自由和谐的讨论环境.
25 楼 Arath 2007-01-18  
我所提出
引用
不知道为什么会提出全局变量构造难以保证,这一点必须是编译器与操作系统协调好的,否则后果不堪设想

是针对
Elminster 写道
全局变量理论上只能保证在 main 函数之前构造完毕,同时一个翻译单元内的全局变量按声明次序构造,除此之外没有任何保证

而来,你就抓住了操作系统这一点和我掰.
一般来说编译产生的最终代码中,需要初始化的数据都会被放在一个initialized data section中,这样操作系统支持的话就可以直接在进入main之前将这个section中的值来初始化对应的内存区域, 但是在某些操作系统中,loader不具备这个功能甚至没有loader,如果这样就需要编译器产生初始化全局变量的代码放在main之前执行.
正是基于这一点我才会说“编译器与操作系统协调好”.
现在明白了不?
讨论技术有争执没有什么,但是要上升到人身攻击那就只能说明自身的粗浅.

到此为止,我不会再回你任何帖子!
24 楼 runes 2007-01-18  
Arath 写道
runes 写道

Elminster的意思应该指的是 全局对象初始化顺序的问题。

这个在不同的编译单元之间是没有任何保证的。

不知道你为啥 能把这个 扯到 操作系统上去?

函数内局部的静态对象,可以在函数第一次被调用时初始化,所以你程序中调用次序就决定了初始化顺序,明白了没?

记的你说自己,搞了个2w行一个的函数,现在我大概能猜出来是啥样了,呵呵....





可见你没有搞过编译器,编译器在很多地方都要依赖于操作系统,如果你要用“扯到”一词只能说明你自己不了解罢了.
重复我的观点,如果编译器无法保证全局变量的成功初始化,那么这个编译器没有人敢用,可以去看看纯C的代码,成千上百的全局变量,如果编译器无法保证这些变量被正确初始化,谁还敢用.
说到初始化顺序,这一点我没有说不对,所以我也不提了,这个完全在于开发人员自己的开发规划.
至于你最后说的那句话似乎只能体现你的“小”而已.



原来是你自己树了个把子打着玩啊!

这里有谁说的没有依赖全局变量初始化问题了,都说的有依赖的,建议你自己仔细再看看帖子。


下面的讨论,就局限于你的靶子 “不依赖于其他全局变量的全局变量初始化问题”

你说 “可见你没有搞过编译器”,嗯,我的确没有搞过,所以不敢装蒜,不过我认为,单一的全局变量初始化应该和
操作系统没有关系。这个应该在编译时就决定了。

听你话里话外的,似乎搞过编译器咯,但愿你不是装蒜,那不妨说说看看,这个全局变量初始化到底哪里依赖于操作系统。
不要告诉我,当程序执行时,需要操作系统的loader先做一把动作,hoho...,这个谁都知道。

至于最后一句吗,仁者见仁,智者见智,当然,淫者也见淫,脑子长在你身上,怎么想,是你的自由.....

23 楼 Arath 2007-01-18  
七猫 写道
BTW:我觉得老谭不算误人子弟吧,很多人都是从他的那本书开始的,我也是。老谭的书我感觉有种朴素美。


谭浩强的那本C书的确很基础,对于新学者还有可以的,但是其中很多代码所传递的喜欢不好,如果一旦适应了就对以后做真正的开发有不好的影响.
我也看过老谭的那本书,后来在图书馆自己借到了另一个C的书,感觉真的是天壤之别,于是马上就丢掉了老谭的书. 现在唯一遗憾的是我把那本书的名字和作者忘记了
另,老谭的书当年创下了中国发行量第一,主要还是归功于各大专院校以其作为课本吧
22 楼 Arath 2007-01-18  
runes 写道

Elminster的意思应该指的是 全局对象初始化顺序的问题。

这个在不同的编译单元之间是没有任何保证的。

不知道你为啥 能把这个 扯到 操作系统上去?

函数内局部的静态对象,可以在函数第一次被调用时初始化,所以你程序中调用次序就决定了初始化顺序,明白了没?

记的你说自己,搞了个2w行一个的函数,现在我大概能猜出来是啥样了,呵呵....





可见你没有搞过编译器,编译器在很多地方都要依赖于操作系统,如果你要用“扯到”一词只能说明你自己不了解罢了.
重复我的观点,如果编译器无法保证全局变量的成功初始化,那么这个编译器没有人敢用,可以去看看纯C的代码,成千上百的全局变量,如果编译器无法保证这些变量被正确初始化,谁还敢用.
说到初始化顺序,这一点我没有说不对,所以我也不提了,这个完全在于开发人员自己的开发规划.
至于你最后说的那句话似乎只能体现你的“小”而已.
21 楼 七猫 2007-01-18  
BTW:我觉得老谭不算误人子弟吧,很多人都是从他的那本书开始的,我也是。老谭的书我感觉有种朴素美。
20 楼 runes 2007-01-17  
Arath 写道
Elminster 写道
七猫 写道
从理论上来都是在第一次使用前构造好,C++的全局变量的构造次序似乎难以保证。


全局变量理论上只能保证在 main 函数之前构造完毕,同时一个翻译单元内的全局变量按声明次序构造,除此之外没有任何保证,所以万一一个全局变量构造时试图访问另一个全局变量的话,很容易出问题。而局部静态变量则保证在第一次调用时构造,这个确定性强多了。


不知道为什么会提出全局变量构造难以保证,这一点必须是编译器与操作系统协调好的,否则后果不堪设想.
另一个问题,如果全局遇到的那个问题那么局部也逃不掉,所以如果从这方面说两者没有什么区别,区别还在于作用域上,当然采用何种方法还在于人的习惯.
无论是全局的还是局部的,其构造都是由编译器产生额外的代码,然后将这个代码放在正确的入口处,这一点任何C/C++编译器都不会有什么大区别.


Elminster的意思应该指的是 全局对象初始化顺序的问题。

这个在不同的编译单元之间是没有任何保证的。

不知道你为啥 能把这个 扯到 操作系统上去?

函数内局部的静态对象,可以在函数第一次被调用时初始化,所以你程序中调用次序就决定了初始化顺序,明白了没?

记的你说自己,搞了个2w行一个的函数,现在我大概能猜出来是啥样了,呵呵....



19 楼 Arath 2007-01-17  
grantren 写道
Arath 写道
runes 写道
1.实话说,我没有看出来,哪里有偏门的地方

2.别那么瞧不起使用局部静态变量,有时很有用,比如meyer singleton


没有瞧不起的意思,但是这种写法不是所有人都能够理解清楚的。
程序除了能够正确实现功能的同时也希望能够较为容易的被他人所看懂,使用一些生僻或者歧义的语法或功能(我就简单说为语法偏门)是很大阻碍.
当然不是说不需要了解这些,如果了解了那么至少对自己是一个提高,只是不建议使用.
还有就是这些语法偏门都可以被直观易懂的语法或功能替代的时候,为什么要用呢(特殊场合除外)?



局部静态变量本来就是这样的,老谭的C程序设计上就有而且很多工程中就这样用,这不算是偏门吧。
其他的话倒是同意。


老谭。。。他被人称作误人子弟的!
18 楼 Arath 2007-01-17  
fullfocus 写道
zeze.... 这次有学到很多....
还有一个疑问静态局部变量创建以后是放在什么存储区呢?


静态局部变量只是限制了其作用域,所以和全局的一样存放在一起.
17 楼 Arath 2007-01-17  
Elminster 写道
七猫 写道
从理论上来都是在第一次使用前构造好,C++的全局变量的构造次序似乎难以保证。


全局变量理论上只能保证在 main 函数之前构造完毕,同时一个翻译单元内的全局变量按声明次序构造,除此之外没有任何保证,所以万一一个全局变量构造时试图访问另一个全局变量的话,很容易出问题。而局部静态变量则保证在第一次调用时构造,这个确定性强多了。


不知道为什么会提出全局变量构造难以保证,这一点必须是编译器与操作系统协调好的,否则后果不堪设想.
另一个问题,如果全局遇到的那个问题那么局部也逃不掉,所以如果从这方面说两者没有什么区别,区别还在于作用域上,当然采用何种方法还在于人的习惯.
无论是全局的还是局部的,其构造都是由编译器产生额外的代码,然后将这个代码放在正确的入口处,这一点任何C/C++编译器都不会有什么大区别.
16 楼 fullfocus 2007-01-16  
zeze.... 这次有学到很多....
还有一个疑问静态局部变量创建以后是放在什么存储区呢?

相关推荐

    Essential C++中文版pdf

    Essential C++中文版pdf

    Essential+C++中文版.pdf

    Essential+C++中文版.pdf Essential+C++中文版.pdf Essential+C++中文版.pdf

    Essential C++中文版

    Essential C++中文版Essential C++中文版Essential C++中文版Essential C++中文版Essential C++中文版Essential C++中文版Essential C++中文版Essential C++中文版Essential C++中文版Essential C++中文版Essential ...

    Essential C++中文版.pdf

    Essential C++中文版,对于入门之后有点基础的小伙伴看,会很不错。这是我从我们主程那边拿来的文档,主程是一个比较吊的程序员,不只是懂c/c++其他还懂好几门的那种。

    Essential C++ 中文版

    Essential C++ offers a fast-track to learning and working with C++. This book is specifically designed to bring you up to speed in a short amount of time. It focuses on the elements of C++ programming...

    Essential C++中文版+完整源代码+习题答案

    Essential_C++ 程序源代码 本書的所有程式,以及習題解答中的完整程式碼,皆可線上取得。你可以在 Addison Wesley Longman 的網站(www.awl.com/cseng/titles/0-201-48518-4)或我的個人首頁(www.objectwrite.com)...

    Essential C++中文简体版 电子书

    Essential C++中文简体版 电子书,详细讲解了C++的基础,适合初学者学习,也是学习C++必看的几本书籍之一。

    Essential C++ 中文版.rar

    Essential C++ 中文版.rar 解压密码是: www.infoxa.com 【注意】

    Essential C++中文版(侯捷译)

    本书以4个面向来表现C++的本质:procedural(程序性的)、generic(泛型的)、object-based(个别对象的)、object-oriented(面向对象的)。本书的组织围绕着一系列逐渐繁复的程序问题,以及用以解决这些问题的语言...

    Essential C++中文版(详细书签)

    Essential C++中文版(详细书签)

Global site tag (gtag.js) - Google Analytics