目录
一. static
1. 作用
2. 一些例子帮助大家更深刻的理解static的几个作用
(1)修饰局部变量:
(2).修饰全局变量和函数
二. const
1. 作用:使得变量不允许被修改, 提高代码的健壮性。
2. 本质:给编译器看的, 在编译阶段起作用。
3. 常见考题:常量指针和指针常量
4. 一些例子
三. volatile
1. 先说作用
2. 编译器的过度优化指的又是什么呢?
3. 总结:
一. static
1. 作用
(1)修饰全局变量:它和普通全局变量没区别, 唯一的区别就是使用static修饰的全局变量无法被同一个工程中的其它源文件使用extern关键字访问, 限定了该全局变量的作用域在该文件中, 总结就是从整个工程都可以访问变成了定义该静态全局变量的源文件。
(2)修饰函数:和上述修饰全局变量的作用差不多, 使用static修饰的函数限定在了该模块内使用, 也可以说是定义该静态函数的源文件中。
(3)修饰局部变量:和普通的局部变量的属性差不多, 唯一的不同是它的生命周期从普通的函数调用开始到函数执行完结束, 变成了整个进程结束后它的生命周期才结束,简单来说就是普通的局部变量内存分配在栈区, 调用完自动销毁回收内存, 而使用static修饰的局部变量的内存是分配在全局区(数据段, 静态区),程序运行结束后才释放内存。
2. 一些例子帮助大家更深刻的理解static的几个作用
(1)修饰局部变量:
有这样一个例子, 我想实现一下输入整数输出字符串, 大家别看代码的实现, 就看static的使用场景就好, 别偏题了。
在下面这个实列中我们可以看到在函数itoa中定义了一个局部数组, 将输入的整数处理完之后, 我想返回数组的首地址, 并在主程序中打印它,运行后出现错误, 见图一。
#include <stdio.h>
char *itoa(int num);
int main(int argc, const char *argv[])
{
int num;
char *p;
printf("please input your want convert digit:");
scanf("%d", &num);
p = itoa(num);
puys(p);
return 0;
}
char *itoa(int num) {
char arr[128]; //大家注意看这里
int i = 0, j, k;
while(num) {
j = num % 10;
num = num / 10;
arr[i] = j + '0';
i++;
}
k = i - 1;
i = 0;
while(i < k) {
j = arr[i];
arr[i] = arr[k];
arr[k] = j;
i++;
k--;
}
return arr;
}
这个时候我们就可以加上static关键字修饰一下数组arr, 使其变成函数体内部的全局变量, 延长生命周期,让主函数拿到它的地址。
static char arr[128]; //大家注意看这里
(2).修饰全局变量和函数
我们写出三个文件, 1.c, 2.c, main.模拟在过程中不同的模块。
/*a.c*/
int a = 10;
/*b.c*/
static int a = 100;
/*main.c*/
#include <stdio.h>
exterm int a;
int main(int argc, char *argv[])
{
printf("a = %d\n", a);
return 0;
}
请问大家, 主函数中访问到的a的值是多少?
答案:访问到的值是a.c中没有用static关键字修饰的a, 作用假设我们是一个小组, 每个人负责写不同的函数模块, 不能确保每个人定义的全局变量的变量名都不同对吧, 所以往这个例子上想static修饰全局变量的作用就是防止全局变量重名的问题, 造成编译器不知道访问哪一个, 出现链接错误。
static修饰函数也是类似的作用。
二. const
1. 作用:使得变量不允许被修改, 提高代码的健壮性。
2. 本质:给编译器看的, 在编译阶段起作用。
3. 常见考题:常量指针和指针常量
*p指向变量的值不能修改, 错误:(*p)++
p的地址不能被修改 错误:p++
*p和p的地址都不能被修改 错误:任意一种都是错误的写法
4. 一些例子
区分别的函数拿到我这个变量是进行读还是写操作, 以此判断要不要加上const关键字修饰。
如果只是读操作,建议加上const。
三. volatile
1. 先说作用
防止编译器的过度优化, 不去缓存器里取值, 都是去取真实地址里面的值
使用场景:
i.访问特殊功能寄存器
ii.线程间共享的全局变量
iii.任务与中断共享的全局变量
2. 编译器的过度优化指的又是什么呢?
假设我写出这样的代码:
int a = 10;
int b = a;
int c = a;
在arm里代码的执行逻辑是这样的, 先取出变量a中的值到cpu的缓存器中, 在把缓存器中的值赋值给b, 我们写的代码是不是又想取出a的值给c, 那是不是又要重复a赋值给b的流程先取出a的值到缓存器中再赋值给c, 这样效率就有点小低了。
3. 总结:
所以编译器的优化就是这一步, 它分析出我们的代码没有修改真实地址a的值和缓存器中的值, 那么它就直接把缓存器中的值给变量c了。
这样来看这样做是好事, 但请大家记住一下三种情况对于我们来说就不是好事了, 因为变量a的值, 也就是真实地址中的值在一下三种情况中会出现被修改的情况, 这样的话, 变量c拿到的值就不是a的真实值, 而是没有被修改之前缓存器中的值。
i.访问特殊功能寄存器
ii.线程间共享的全局变量
iii.任务与中断共享的全局变量
各位看官切记切记, 不然当项目因为这种问题出现bug的时候是无法发现, 无法调式的。