一 Volatile 关键字
volatile告诉编译器不用对其修饰的变量进行优化。例如从缓存里读取该变量导致无法捕捉到该变量的变化,或者一些其它的优化。被volatile修饰的变量,CPU每次访问该变量时都会从内存中读取出该变量。
1 volatile主要用于以下情况:
1 当这个变量可能由硬件改变时
2 当这个变量可能由中断改变时
3 当这个变量被多个执行单元同时访问改变时【注意:volatile不能保证多线程下变量的原子性,还是需要使用互斥锁保证】
2 编译器可能对变量的一些优化方式:
1 寄存器分配:编译器可能会将变量的值存储在CPU寄存器中,以减少内存访问次数。这种优化可以显著提高性能,因为寄存器访问比内存访问快得多。例如,如果一个变量在一个循环内频繁使用,编译器可能会选择将其值保存在寄存器中,以避免每次循环都需要从内存中读取。
2 值的缓存:如果编译器认为变量的值在某段代码中不会改变,它可能会缓存该值,而不是每次访问都从内存中读取。但可能会错过外部事件或中断引起的值的变化
3 常量传播:如果一个变量被赋值为一个常数值,并且在后续代码中不再改变,编译器可能会直接替换掉对该变量的引用,用该常数值代替。这种优化减少了不必要的内存访问,但可能导致错过外部事件导致的值的变化。
二 static关键字
1 static关键字修饰变量
static可以用于修饰变量和函数。修饰变量时,分为【静态全局变量】和【静态局部变量】。
静态全局变量 | 静态局部变量 | |
---|---|---|
生命周期 | 整个程序执行周期都存在 | 整个程序执行周期都存在 |
作用域 | 限定于定义它的文件 | 限定于定义它的函数 |
初始化 | 若未初始化,初值为0 | 若未初始化,初值为0。 |
若显示初始化,则仅在函数第一次被调用时初始化 |
static修饰局部变量,只有在调用该函数时初始化一次
// static修饰局部变量,只有在调用该函数时初始化一次
#include<stdio.h>
void text()
{
static int a = 0;
a++;
printf("%d ", a);
}
int main()
{
int i= 0;
for (i= 0; i < 10; i++)
{
text();
}
return 0;
}
上述程序打印出的内容为 a的值从1加到10
2 static 修饰的全局变量没用【外部属性】
即static修饰的全局变量和函数,只能在本文件中使用。
注意:如果在同一个 .h 文件中定义的static变量,不同.c文件使用该static变量时会是不同的变量。
例如在一个头文件 “head.h” 中定义了静态变量static int num,尽管在不同.c文件中都可以调用num变量,但在一个.c文件中改变num变量不影响其它num变量,他们使用的num变量地址都不相同。
例如:
head.h文件
static int num = 0;
void func();
program1.c 文件1
#include"head.h"
void func()
{
cout << "文件1中num初始化值:" << num << endl;
num++;
cout << "文件1中num++后值:" << num << endl;
cout << "文件1中num地址:" << &num << endl << endl;
}
program2.c 文件2
#include"head.h"
int main()
{
func();
cout << "文件2中num初始化值:" << num << endl;
cout << "文件2中num地址:" << &num << endl;
return 0;
}
可以发现文件1和文件2中的num地址是不同的。
static修饰的全局变量不进入符号表
static修饰的变量在全局数据区分配存储空间,且static修饰的变量不进入符号表,因为static修饰的变量只在本文件中使用。因此,在编译器的链接阶段不同文件中定义的static变量不会重读。
总结下stattic和extern区别
搞清楚static变量的存储区
三 C语言模型基础
C语言编译的四步
1预处理 -> 2编译 -> 3汇编 -> 4链接
1 预处理
预处理阶段,对于#include关键字,直接将#include的文件在代码该处展开。
使用gcc编译器 [ -E ]选项,可以查看预处理后的结果。
gcc -E -o main.i main.c
其中,main.i 就是预处理后的结果。
2 编译
将预处理后的源代码转换为汇编代码
3 汇编
将汇编代码转换为机器语言【二进制】,生成【符号表】信息