c++学习 5 预处理
一 内存分区
内存的分区变量存储,一般可以分为以下五个区,它们分别是:
可读可写 堆区:使用malloc、calloc、realloc、free以及c++里面的new和delete去动态申请。
可读可写 & 栈区:局部变量,函数的形参,以及大于4B的返回值。
可读可写 & 全局区:普通全局变量,静态全局变量和静态局部变量。
只读 文字常量:数值常量,字符常量,字符串常量,符号常量。
只读 代码区:代码的二进制指令。
二 变量的存储
普通局部变量
定义形式:在{}里定义的普通变量,叫做普通局部变量。
存贮区域:栈区
作用范围:每一个普通的局部变量作用范围是变量所存在的离它最近的一个完整的整个大括号范围。
生命周期:每一个普通局部变量的生命周期就是它所在的复合语句执行的周期。
一旦程序的执行(从上往下)离开了该普通局部变量的大括号范围,该普通局部变量将会被立刻回收,同时释放它所占用的内存
example:
void test01()
{
int num1=10;//
{
int num2=100;
int num1=20;
cout<<num1<<;//输出结果为20。
cout<<num2<<;//输出结果为100。
}//num2的生命周期结束。
cout<<num1<<;//输出结果为10。
cout<<num2<<;//无效语句,会报错。
}//num1的生命周期结束。
普通全局变量
定义形式:在函数外定义的普通变量(在复合语句外定义的变量)、
存储区域:全局区
作用范围:当前源文件以及其他源文件都有效。
生命周期:整个进程
example:
int data=10;
int main()
{
int data=20;
cout<<data<<;//输出内容为20。
cout<<::data<<;//通过作用域访问全局变量,输出内容为10,c语言不支持而c++支持。
}
01_code.cpp
内部有int data=100;
02_code.cpp
内部有函数void app_data() {};
欲在02_code.cpp里使用data=100,必须做一个外部声明:
extern int data;//声明data为int类型,来自于其他源文件。
void app_data()
{
data=data+1;
}
欲在01_code.cpp里使用void app_data函数,同样需要做一个外部声明:
extern void app_data(void)
int main()
{
app_data();
return 0;
}
静态局部变量
定义形式:在大括号{}内加上static的变量就是静态局部变量。
存储区域:全局区
作用范围:当前所在的大括号{}复合语句都有效
生命周期:整个进程有效
example:
void test04()
{
int data1=10;//普通局部变量
static int data2=100;//静态局部变量
data1++;
data2++;
cout<<data1<<endl;
cout<<data2<<endl;
}
int main()
{
test04();//data1=11,data2=101。
test04();//data1=11,data2=102。
test04();//data1=11,data2=103。
test04();//data1=11,data2=104。
//data1一直被反复定义然后释放,而data2只被定义了一次且没有被释放。
}
静态全局变量
定义形式:在函数范围外,加上static修饰定义的变量就是静态全局变量
存储区域:全局区
作用范围:只能在当前源文件使用,不能在其他源文件里使用。
生命周期:整个进程。
example:
int data1=100;//普通全局变量
static int data2=200;//静态全局变量
静态函数和全局函数
全局函数(一般函数默认为全局函数)
在当前源文件和其他源文件都可以使用,在其他源文件里使用需要加上extern外部声明。
静态函数(加上static修饰的函数)
属于静态成员,只能在当前源文件内使用。
头文件包含
<>和" "都可以用于读取头文件,但是<>是先从系统目录寻找,而" "是优先从当前目录寻找,所以最好是用<>来读取系统头文件,而" "用于读取自定义的头文件。
宏定义#define
定义形式:使用关键字define进行定义,就叫做宏。
作用范围:从当前定义开始,到整个文件结束(#undef可以结束宏的作用域)
宏定义后面不要加分号!
宏定义尽量使用大写,为了和普通变量分开
example:
#define PI 3.14
if(PI > 3)//相当于if (3.14 > 3)
{ 语句; }
//在预处理的结果,将后面有PI的地方全部用3.14来替换,而且是十分生硬的替换。
不带参数的宏:
#define PI 3.14
#define M 100
#define MY_STR "I love China"
带参数的宏(宏函数)
#define MY_MUL(a,b) a*b
cout<<MY_MUL(10,20)<<://输出的是“10*20”
宏的参数不能有类型,#define作为一个预处理,仅仅只是起到了一个替换的作用,并不能开辟空间
#define MY_MUL(int a,int b) a*b//会出错的
宏也不能保证参数的完整性,原因如上
#define MY_MUL(a,b) a*b
cout<<MY_MUL(10+20,10+30)<<;//结果为10+20*10+30,和我们的意图完全不符合
可以使用加小括号()的方式去保持参数的完整性
#define MY_MUL(a,b) (a)*(b)
宏不能作为结构体、类的成员
#define MY_MUL1(a,b) a*b
#define MY_MUL2(a,b) (a)*(b)
cout<<MY_MUL2(MY_MUL1(10+10,20+20),MY_MUL1(10+10,20+20))<<;//结果为8220
宏函数和普通函数的区别
带参宏被调用多少次就会展开多少次,执行代码的时候没有函数调用的过程,不需要压栈弹栈,所以带参宏是浪费了空间,因为展开多次,节省了时间;
带参函数,代码只有一份且存在代码段里,调用的时候去代码段取指令,调用的时候要压栈弹栈,有一个调用的过程。所以带参函数是节省了空间,浪费了时间。
Tips:
1.&代表可做取地址操作的意思;
2.堆区需要通过相应的接口函数才能得到地址;而文字常量区呢不允许取地址,而且字符串常量是通过特殊符号得到了它的地址;代码区我们通过函数名去得到他的地址,但是一般不能用这个去取;
3.每一个大括号{}包括其中内容在内的,这个整体被称为一条“复合语句”;
4.普通局部变量如果不初始化,那么其内容将会是不确定的(随机值);
5.普通局部变量同名遵循就近原则;
6.普通全局变量不初始化,内容默认为0;
7.全局变量和局部变量同名,优先使用局部变量;
8.extern做外部声明声明函数的时候,需要带上返回值类型,函数名和形参就够了;
9.静态局部变量若不进行初始化,内容默认为0;
10.多次反复在一个进程内定义相同一个静态局部变量,其定义只有第一次生效,后续对此的相同定义都无效(因为没有被释放过,再次定义的话就会产生冲突,所以编译器忽略了该定义的代码);
11.全局区的内容特点为未初始化的变量默认为0,且生命周期为整个进程;
12.静态全局变量若不进行初始化,内容默认为0;
13.在当前源文件内使用的好处,就是避免命名冲突(在多项目合并为一个项目的时候);
14.编译四阶段:预处理,编译,汇编,链接;
15.可执行文件,在从运行到结束,这一整个动态的过程就叫做进程;并且需要占用内存空间;
16.宏没有作用域的限制,只在当前源文件内有效。