关于C/C++中全局变量的初始化问题的深入思考

上一篇 / 下一篇  2012-10-26 10:00:44 / 个人分类:C++

CU/xv?%rs9i{0  前言:51Testing软件测试网 D X.R Ga,X+]4z0V

51Testing软件测试网+I.O]f;a.ay@.h

  前日,在一次C++课程上,刘老师在举例说明构造函数和析构函数的功能时,提到了 全局变量初始化时的构造函数的行为。构造函数在main函数之前初始化全局变量。当然在C++下我是深信不疑的。但随后老师声称C语言下的全局变量也是如 此,因为C没有构造和析构函数,所以我们无法看到这一过程,在C++下可以在构造和析构函数中向屏幕打印信息,进而可以观察全局变量的初始化和生存期。

M2w9MQ*}5cp7l0

Z'C!oV,TJM0  这个观点无疑使我心头一震,作为C的痴迷者,长期以来在我头脑中的印象是,全局变量在编译期就完成初始化了。难道我的观念是错误的?!难道C真的也是在main函数之前,在程序运行初期才初始化?!51Testing软件测试网/L I z*l3A(y!C,W4p

hP}L+{qIe-u+S0  于是我翻看了《C语言参考手册》这本书上没有明确的答案,再翻看著名的K&R的《C程序设计语言》中只有括号里面的一句话“在概念上.......”也是含糊其辞。(现在想想这个问题可能和编译器有关,所以丹爷爷也没说明太多)

\[9?.S}E B051Testing软件测试网@/|3t l)I+`b2U

  在网上查询了一下,关于这个问题,持什么观点的都有,没有一个权威的答案。51Testing软件测试网(pj7B\2ig:Y/wu

51Testing软件测试网3t$c-h%~b,p

  只能靠自己了,动手实验!51Testing软件测试网e&f)hgd^ s"qx` LH.? S

51Testing软件测试网9u4T7L/u!k#l ~

  先给出我的结论:51Testing软件测试网5G,JC l5a%h

51Testing软件测试网-D(NL.H;iw-oVQ Q-N\

  C和C++中的一般全局变量(不包括类class)是在编译期确定的初始值,而不是在程序运行时,在main函数之前初始化的。51Testing软件测试网 t/F ],~K+v:[^ `1~

51Testing软件测试网4n4I]bQ:Y`2b4Os`

  C++中的类的全局变量是在程序运行时,在main函数之前初始化的。

5LE0ad0X:eT051Testing软件测试网 dA6|;A)g

  预热知识:

M o k Mn!^Lc3L}051Testing软件测试网wI5t&knnb_

   C或者C++语言,明面上的入口函数是main(argc,argv),或者tmain、wmain、WinMain等等。但实际上,是C Runtime的startup代码中的void mainCRTStartup(void)函数,调用了编程者写的main函数。这个函数定义在VisualC++安装目录的crt\src\目录下的某 个.c文件中(视VC++的版本不同,存放的文件也不同)。它在执行一些初始化操作,如获取命令行参数、获取环境变量值、初始化全局变量、初始化io的所 需各项准备之后,调用main(argc,argv)。main函数返回后,mainCRTStartup还需要调用全局变量的析构函数或者 atexit()所登记的一些函数。往深里说,是在链接生成可执行文件时,告诉链接器这个可执行文件的entry就是mainCRTStartup。当 然,我们也可以对编译器进行设置,使其不插入mainCRTStartup函数代码51Testing软件测试网9BM+Ro{ D

51Testing软件测试网/Us|Q x br1T&Pl+l

  以VC++6.0为例设置:Project->Settings->Link 在Category中选择Output,在Entry-point symbol中填上main 即可。

k m"f$c` yev0

W6i u4G T@?0  -------------------------51Testing软件测试网M+Bh!cZ?

51Testing软件测试网*n)yo%Xv+rRa\e

  实验一:51Testing软件测试网;gs"E.vIy4cb(~

51Testing软件测试网0V4j1RrB_

  1、C语言环境下:

E0H]A0Y9ao.E051Testing软件测试网%I+Q,l7R%sP%Rp#B@

  实验准备:

8kqD\;r0
int a ;51Testing软件测试网$N'TH'XL&H6l7U
int main(void)51Testing软件测试网:p4x_Dn S
{
/Vtn#|_+Q.n0       return a+3 ;51Testing软件测试网hw5YL$L
}
51Testing软件测试网t%UQ:D Ix!Kf^6m

  在编译器中设置入口函数为main(具体方法见上面)

Hv)M7nt@\;Uv051Testing软件测试网 YBRbXK[QG

  这样,我们让编译器生成的程序,直接从main函数中进入,而不是先执行mainCRTStartup函数做一些准备工作51Testing软件测试网P5@"c(QO.d

51Testing软件测试网\P$Ya?u?%H

  结果预测:

YKl X2ye0

vT&Ex(t0  这样,如果函数返回的是3,则说明此全局变量是在编译期就被初始化为0了,如果函数返回的是其它数字,则说明此全局变量是在程序运行时,main函数运行前进行的初始化。51Testing软件测试网0j} L;_%q;u$`

51Testing软件测试网5cz/|"w.R @

  实验结果:51Testing软件测试网4yL-RgYH_T

51Testing软件测试网0O!r ]G\e#y"kD%m+y

  进入控制台(运行cmd命令),运行编译后的程序(因为程序没有向屏幕输出结果,我们看不到任何现象),继续输入命令:echo  %ERRORLEVEL% 则显示3,此即为函数的返回值。51Testing软件测试网?-}*`!Yo'q} U

51Testing软件测试网P] s1?J&C"Sl

  (echo是显示其后的值,系统把前面运行的程序的返回值放在%ERRORLEVEL%中,故我们可以通过此方法获得主函数的返回值)

0U&]@-ct Y0

i,Zd2e.o$k0  同理:对于结构体全局变量51Testing软件测试网1m RC6H&V}

8p7Psf4^~0

#KV7TLSa0
struct A51Testing软件测试网;e%bS A!^ N&U8PJ
{
H+hg h;kPyebH0       int a ;    51Testing软件测试网A}v-?(TYv-]/{ Kv
} sTest;
m+C)Aa$d J7WV2I0int main(void)
Z$?$q7_We(}p0{
S[qcb}s2g0       return sTest.a+3 ;51Testing软件测试网J[h{Ew
}

?'\`P%N0\u Y0}#Jx0  函数也返回3。51Testing软件测试网 H(nHJtp

3Gv"V-y ]0  实验结论:

h6wuRC0b051Testing软件测试网5C4n@#NYG

  在C语言中,全局变量是在编译期完成初始化的。51Testing软件测试网iXV*rYD

51Testing软件测试网 ~!a JMI3|.O$Z9S3h"Z C

  (在本实验中我们没有使用I/O函数把结果打印出来,因为I/O函数的调用之前必须要初始化内存中的某堆空间,而这个工作是由main函数之前的mainCRTStartup函数来做的。而我们设置让编译器跳过这个函数,故会在运行时出错。)51Testing软件测试网6AL U7^7|6mt)g Ys6J%b

7X"xzb5ir |4c S0  实验二:51Testing软件测试网.FHQwg!qv#X6D N Q

x? qE [j/Syc0  C++语言环境下

{To,@$k]X{ o0

S*|\ReF0  实验准备:

}h:^1ag H-s0

P#};vHza0

8I|C3asC5yu0
class A
1qS4}n ?S@k0{51Testing软件测试网*Q}6C6Y4B
public:
(gS$qA m R0       int a ;    
%a Hc/g oI P0       A(){a=10;}51Testing软件测试网'nB6SpQ_8Q
       ~A(){}
"S}?wa(MJ(w0} ;51Testing软件测试网#x7dj/tA"a*e}
A cTest ;51Testing软件测试网Se%W%u0},Z#w$V5O-q1~"p
 51Testing软件测试网 {Z&k l;x
int main(void)51Testing软件测试网~!F,f(F"R N
{51Testing软件测试网]kS8P5J:|{,Pz
       return cTest.a ;
U"EC9r3i.P!j0}
51Testing软件测试网!Cqc v l

  结果预测:

^g/Kw_nns7sz~0

^"j iMu1E3yV(\0  这样,如果函数返回的是0,则说明此全局变量是在编译期就被初始化为0了,如果函数返回的是其它数字,则说明此全局变量是在程序运行时,main函数运行前进行的初始化。51Testing软件测试网0onH5`M.S

51Testing软件测试网M`%ePCp/Cv/~

  实验结果:

+y(a5q/k,Yz0`051Testing软件测试网+L_?4a+b

  在编译器中设置入口函数为main,主函数返回一个其他值

;W"Ch^&gq2Y+]l051Testing软件测试网 P|*D}:w1w5i

  在编译器中设置入口函数为默认,主函数返回值为1051Testing软件测试网"])d&A5V'l

51Testing软件测试网L%@xhcK%h+K)r\

  实验结论:51Testing软件测试网C w'Z uA0U

51Testing软件测试网)x]1f[q1o"O

  在C++中,类(class)的全局变量是在程序运行期,main函数开始之前,调用类的构造函数完成初始化的。

0n Bj-U x6|0

(};v\\3L/Q6`!E0  同理:51Testing软件测试网KO}8c2w4l

51Testing软件测试网$d'V;y;g^\a!U)I

  把C中的代码放到C++下实验

#On|J~I051Testing软件测试网$h Rs$~.z h.z0tX4t

g4t O#]w$c#O0int a ;
/W!C:hgh0int main(void)51Testing软件测试网^\0`J^:woLJ6cjC
{
8s,I ?5gzw&]&q0       return a+3 ;
D'D5B*}2na*B!um0}
"qj n;P"@0

(y,z-jO-Q)lA~0  结果与C的结果相同。51Testing软件测试网7A6N p;R;ZV,E8i

*F!eo~;mm0  说明:在C++中一般全局变量的初始化(类除外),是在编译期完成的,而不是在运行期完成。(与C语言规则相同)51Testing软件测试网 b$d s!V8_*{W3D%W

51Testing软件测试网/zw n U"N

  mainCRTStartup函数不管一般全局变量的初始化,它管理类(class)的全局变量的初始化,调用类的析构函数。51Testing软件测试网(v7N5\2yz[G%ej

"_U8oi0q0  编译器会在编译时,初始化一般全局变量为0.51Testing软件测试网S)N y!I5e(lW

G{x9fY0  另:具有全局生命期的局部静态变量的初始化,与局部变量相同都是在运行时,执行到该初始化语句完成初始化的,只是局部静态变量只初始化一次。51Testing软件测试网x ^3PIebW

51Testing软件测试网D#^ J3NI [1t+{Dw

  后记:51Testing软件测试网1h0[ }"J!N

"M"U$Nq2K8XN0  1、程序不是从主函数开始执行的,而是先要执行一些启动代码。(现在明白为什么要在在嵌入式软件编程时要在工程中添加类似于75x_init.s和75x_vect.s这两个汇编文件了吧)51Testing软件测试网,D5D6j W6UmFT

51Testing软件测试网fYwO-IQ+g ^

  2、你应该给主函数以返回值。实际上标准C只规定了两种形式的main函数:

Fn,dLdX(r4wO051Testing软件测试网6j d1g\9M

  int main(void) 和 int main(int argc, char *argv[])51Testing软件测试网'q6W6rZY

51Testing软件测试网V^ZWQ'@r

  main返回0,告诉系统程序正常终止,返回非零值告诉系统程序异常关闭.51Testing软件测试网c1[a6\"\}Y7RZ

51Testing软件测试网2Z C-V${l-WM9_

  其作用:我们可以利用程序的返回值,控制要不要执行下一个程序。51Testing软件测试网$tttp ~&_4?p0n

51Testing软件测试网wsYQ y1\l

  例:程序名&&DOS命令

*?/B:U}Z}0

;}Z5l `"Y$y0QX3_ _0  前面的程序正常执行后才执行后面的DOS命令。当然我们也可以用其它的逻辑符把程序和命令组织起来,来实现复杂的功能。

Q9M5yM K | IS B0

0e tlO1K*Eej0  (UNIX中的shell命令也有类似功能)51Testing软件测试网9Uu7FTiK`q

51Testing软件测试网 |;o E(~H

TAG:

 

评分:0

我来说两句

Open Toolbar