目录
1. C/C++中内存大致分的三个区域
栈区(stack):由编译器自动分配释放。
存放 :局部变量、形参、返回值。
堆区 (heap): 由程序员分配内存和释放。
调用函数 :malloc() free()等。
静态区 :通常是用于那些在编译期间就能确定存储大小的变量的存储区,全局变量和 静态变量。
2. 关键字static
在C语言中:
static是用来修饰变量和函数的
1. 修饰局部变量-称为静态局部变量
2. 修饰全局变量-称为静态全局变量
3. 修饰函数-称为静态函数
静态局部变量
示例:
大家来来看这段代码
局部变量a,在没有static
修饰之前局部变量a是存放在栈区的。所以每次出局部范围就销毁(把空间还给操作系统)。然后,调用时重新创建初始化。
那我们把局部变量a加上static关键字修饰成静态局部变量,会怎么样呢?
//static 修饰局部变量的时候 //本来一个局部变量是存放在栈区的,如果被static修饰就存储到静态区了 //static 修饰局部变量改变了变量的存储类型(位置),使得这个静态变量的生命周期变长了,直到程序结束才结束 //但是作用域不变 void test() { static int a = 5;//静态变量的 a++; printf("%d ", a); } int main() { int i = 0; while (i < 10) { test(); i++; } return 0; }
这里static
关键字把变量a修饰成了静态变量
,所以变量 a a a 本来是存放在栈区的,但是由于被修饰成静态变量所以被存放在静态区
了。
静态区变量的特点:
创建好后,直到程序结束才销毁
这里说明了:静态变量a在程序编译的时候就自动创建好了,并且已经完成初始化了
而没有修饰呢:
没有修饰之前必须进入函数体中初始化语句才会完成初始化。
静态局部变量的作用域和生命周期
前面我们说了静态变量是创建完成后,直到程序结束才销毁。
所以,静态变量的生命周期是整个工程。
而静态局部变量的作用域呢?
虽然我们把变量a修饰成了静态局部变量但是他本质上还是个局部变量
所以他的作用域不变还是它所在的局部范围
静态全局变量
我们都这样全局变量的作用域是
整个工程
可以看到只要在一个工程内,不同.c
文件之间只要(声明)也可以调用。
说明:全局变量具有外链接属性。
但是我们用static关键字修饰成静态全局变量看看
说明:static 修饰全局变量
- 改变了这个全局变量的链接属性,由外边链接属性变成了内部链接属性
- 就是这个静态变量只能在自己所在的源文件内部使用,不能在其他源文件内部使用了
- 感觉像是作用域变小了
静态函数
static修饰函数和修饰全局变量是一样
用static修饰函数了之后
说明:用static修饰函数
- static 修饰函数和static修饰全局变量是一样的
- 函数是具有外部链接属性的,但是被static修饰,就变成了内部链接属性
- 使得这个函数只能在自己所在的源文件内部使用,不能在其他文件内部使用的
3.#define 定义常量和宏
#define 定义常量
#define M 100 int main() { int arr[M] = {0};//100*4 = 400 int m = M; printf("%d\n", sizeof(arr));//400 printf("%d\n", M); printf("%d\n", m); return 0; }
这里#define 定义的是M这个标识符常量
- 以后我们在碰到
M
的时候编译器在编译期间,就会自动替换为常量100
。 - 通常在定义数组时使用或者重复值时。
#define 定义宏
我们来看一下宏是怎么定义的:
和定义函数非常相识
但是宏没有函数的返回类型和参数类型
还是很不一样的,宏的实现体一般都是表达式
那么宏是怎么调用的呢?和函数有什么区别嘞?
//宏 #define ADD(x, y) ((x)+(y)) //函数 int Add(int x, int y) { return x + y; } int main() { int a = 10; int b = 20; int c = ADD(a, b); //int c = (a)+(b); printf("%d\n", c); int d = Add(a, b); printf("%d\n", d); return 0; }
我们可以看到宏的调用也和函数产不多,但是
int c = ADD(a, b);这段代码调用宏的本质是替换
int c = (a)+(b);在编译的时候就把ADD这个宏替换成这样
而函数调用是
把实参传到函数形参里面进行计算,然后在返回值
4.关键字 typedef
typedef 顾名思义是类型定义,这里应该理解为类型重命名。
示例:
//将unsigned int 重命名为uint_32, 所以uint_32也是一个类型名 typedef unsigned int uint_32; int main() { //观察num1和num2,这两个变量的类型是一样的 unsigned int num1 = 0; uint_32 num2 = 0; return 0; }