首先来了解下,可执行文件加载进内存后形成的进程在内存中的结构:
有堆区,栈区,BBS区(未初始化数据区),数据区(初始化了的),代码区。
其中:
栈区:由编译器自动分配释放,存放函数的参数值、返回值和局部变量,在程序运行过程中实时分配和释放,栈区由操作系统自动管理,无须程序员手动管理。
堆区:堆是由malloc()函数分配的内存块,使用free()函数来释放内存,堆的申请和释放工作是由程序员控制,容易产生内存泄漏。
BBS区:存放的是未初始化的全局变量。
数据区:存放已经初始化的全局变量,静态变量(包括全局和局部的),常量数据。
代码区:存放CPU执行的机器指令,代码区是可以共享的,并且是只读的。
eg
#include<stdio.h>
#define CONST 100
int *p1;
int a[2];
int b;
static int c;
main()
{
int d;
static int e;
int f[2];
int *p2;
printf("CONST=%d\n",CONST);
printf("a[0]=%d\n",a[0]);
//printf("*p1=%d\n",*p1);
printf("b=%d\n",b);
printf("c=%d\n",c);
printf("d=%d\n",d);
printf("e=%d\n",e);
printf("f[0]=%d\n",f[0]);
printf("*p2=%d\n",*p2);
}
数据区存放已经初始化的全局变量和静态变量,常量。
**未初始化数据区存放全局未初始化变量。**BSS的数据在程序开始执行之前被初始化为0或NULL。BSS段的变量在目标文件中只占一个符号位,编译器并没有给变量分配空间,所谓的“初始化为0”是指要链接阶段才申请了空间,并随即初始化为0。而已初始化的全局变量要占用目标文件的大小。
换句话说全局变量,静态变量(包括全局和局部的),常量未显式初始化被默认地初始化时0或NULL。故打印*p1出错,因为p1指向NULL地址。
局部的非静态变量未显式初始化时是一个随机的数,一般是个很大的数。
2.对于类类型变量的自动初始化
不论是在全局还是局部作用域,类类型变量都会调用“默认构造函数”进行初始化。
class A{
public:
int value;
A(){
cout<<"Intitialize A"<<endl;
value=3;
}
};
A a1;
int main(){
A a2;
cout<<a1.value<<endl;
cout<<a2.value<<endl;
return 0;
}
输出:
Intitialize A
Intitialize A
3
3
如果类没有显式地定义任何构造函数,则编译器会自动为其生成空参数的构造函数,称为“合成默认构造函数”。“合成默认构造函数”初始化成员的规则有3条:
1.对象在全局作用域或为静态局部对象时,则类的内置成员变量被初始化为0.
2.对象在局部作用域定义时,则类的内置成员变量不被初始化为0.
class A{
public:
int value;
};
A a1;
int main(){
A a2;
static A a3;
cout<<a1.value<<endl;
cout<<a2.value<<endl;
cout<<a3.value<<endl;
return 0;
}
输出:
0
2510836
0
3.对于类类型成员按照其自身的(合成)默认构造函数进行初始化。——重要
class A{
public:
int value;
A(){
value=5;
}
};
class B{
public:
int value;
A a;
};
B b1;
int main(){
B b2;
cout<<b1.value<<"\t"<<b1.a.value<<endl;
cout<<b2.value<<"\t"<<b2.a.value<<endl;
return 0;
}
输出:
0 5
134514784 5
class A{
public:
int value;
};
class B{
public:
int value;
A a;
};
B b1;
int main(){
B b2;
cout<<b1.value<<"\t"<<b1.a.value<<endl;
cout<<b2.value<<"\t"<<b2.a.value<<endl;
return 0;
}
输出:
**0 0
134514736 -1081710584**
4.对于指针成员,如果不在构造函数里显式初始化,则自动会有有个地址,非NULL,故最好把不显式初始化的指针在构造函数里赋值为NULL,避免野指针现象。