C语言中的static const volatile

C语言中的static const volatile

说明

还没有说明

1.static关键字

  • static关键字有两层含义:
    • 1. 修饰的变量的在程序运行时内存常驻
      当static修饰变量时,该变量被编译器分配在内存的全局区,程序运行时,该变量的生命周期伴随整个程序。在裸机开发中,可以认为静态变量的物理地址是静态的,即编译器编译之后,该变量的内存地址就确定了。(带操作系统时静态变量的地址是如何得我并不清楚)
    • 2. 修饰的对象被限制在某个范围内使用
      static用于限定对象的作用范围时,修饰对象可以是变量和函数。
      修饰函数->该函数被限定在该文件内调用,不可以在其他文件中使用extern关键字声明静态函数。
      修饰变量->修饰变量分为两种,修饰全局变量和修饰局部变量。
      1. 修饰全局变量: 全局变量本身就具有上方 ‘1…内存常驻’ 所述的属性,因此当static关键字修饰全局变量时,为其增加了一个限定范围属性,即该变量只能在该文件内调用,不可以在其他文件extern
      2. 修饰局部变量: 局部变量使用范围被限定在子函数中,本身不具有内存常驻属性。其随着子函数的调用由CPU分配内存(得到内存),随着子函数的退出被收回内存(失去内存),得到与失去都是在栈中进行的。而static关键字的作用在于告诉编译器这个变量不要放在栈中,要给我单独在全局区分配内存给它。所以static为局部变量增加了一个常驻内存属性
使用对象 全局变量 局部变量 函数
结果 缩减了作用域(本文件) 延长了生命周期(常驻内存) 缩减了作用域(本文件)

2.const关键字

  • 1.const用于全局变量
    • 限定全局变量为只读变量(常变量),被编译器放在代码段(只读区域),const修饰它本身之后的变量,如:
      const int a = 10;
      int const a = 10;
      
      均为定义了一个int型的常变量,并赋值10.
      但这一点在与指针交织时有点乱,如:
      const int* p = address;		//1
      int const *p = address;		//2
      int* const p = address;		//3
      
      其中1与2是等价的,可见const之后的变量为*p,因为 *p为取值,也就是说const修饰指针p指向的地址的值,这样p也被称为常量指针,即指向常量的指针,指针指向的地址内容不可变
      3则不同,const之后仅仅修饰p这个指针本身,所以p被称为指针常量。指针本身的指向地址不可变。
  • 2.const修饰局部变量,函数形参
    • 限定在该子函数内,不能对const修饰的变量进行赋值,自加,自减等操作,如:
      void foo(const int a, const int* p1, int* const p2){
      	const int b = 10;
      	b = 100;			//错误,对const修饰的变量进行赋值
      	a++;				//错误,对const修饰的变量进行自加
      	a = 1;				//错误,对const修饰的变量进行赋值
      	*p1 = 10;			//错误,对常量指针进行赋值
      	p1 = &b;			//正确
      	*p2 = 10;			//正确
      	p2 = &b;			//错误,对指针常量进行赋值
      }
      
  • 3.const修饰函数返回值
    • 直接看代码
      const int foo()
      {
         return 1;
      }
      
      void main(void)
      {   
        int a = foo(); 				//错误
        const int a = foo();			//正确
      } 
      

3.volatile关键字

  • 防止编译器对一些变量进行优化
    CPU在对操作数进行操作符运算时,操作数的来源有三种:立即数,寄存器操作数,内存操作数.链接: link.

    • 立即操作数: 指令要操作的数据以常量的形式出现在指令中,称为立即数,它只能作为源操作数 。
    • 寄存器操作数: 指令要操作的数据存放在CPU中的寄存器里,指令中给出寄存器名即可。
    • 内存操作数: 指令要操作的数据存放在内存某些单元中,指令中给出内存单元物理地址(实际上指令只给出了偏移地址,段地址采用隐含方式给出,也可以使用跨段方式指出当前段地址)。

    由于寄存器操作数的访问速度更快,编译器会尝试对其中的内存操作数进行优化,将其直接放在CPU的某个寄存器中,比如访问频率很高的循环变量’i’,比如如下代码:

    int i = 0;
    int main(void)
    {
    	while(i < 100)
    	{
    		printf("%d\r\n", i++);
    	}
    	return 0;
    }
    

    由于编译器在检索完程序后,会尝试将i优化为寄存器变量,变为寄存器操作数,仅在首次使用时从内存读取到CPU寄存器。如果发生了,此后while每循环一次,CPU寄存器中的i会加1,而此时内存中的i并不会被CPU重复读取。由于CPU读取自身寄存器的速度快于读取内存的速度,因此这会提高程序的运行效率,同时这个有可能引来灾难,如果外界在CPU不知情的情况下修改了内存中的i值,CPU仍自顾自的以寄存器中的i值进行运算,将会导致无法预料的后果。

    可能的情况有:

    1. 全局i的值可能被其他的中断程序控制。
    2. 全局变量作为临界资源,被其他线程控制。

因此,在上述可能性存在时,应该给变量加上volatile修饰符,告知编译器,每次要读这个变量的值时,都必须去该变量所在的内存中去读取,而不是使用CPU寄存器中的备份。

发布了4 篇原创文章 · 获赞 1 · 访问量 101

猜你喜欢

转载自blog.csdn.net/socbis/article/details/105577265