杂货边角(4):C语言static, inline, volatile, const等关键字解析

1. static关键字解析

ANSI标准规定了C具有32个关键字,其中绝大多数并无特别之处,除了涉及到存储类型的几个关键字,而我们的static关键字便是属于存储类型声明的关键字一类:

1. auto: 声明该变量标识符是存放在栈上的(局部变量的默认修饰符),编译器自动完成,现今不需要手动声明,故而该修饰符几乎不用;
2. register: 声明寄存器变量,我们知道变量存放在寄存器中将会非常有利于关于该变量的读写加速,我们在编译原理的代码优化知道,寄存器使用优化是目前编译器的核心功能,考虑大现今编译器的强大功能,这个修饰符也几乎不用;

3. extern: 声明该变量或函数的定义在其他文件处,全局变量和函数都是默认extern的,故而这也是声明全局变量的初始化全局变量可以不在一起的原因;

4. static: 更改存储位置和访问权限
(1) static修饰局部变量,是为了改变局部变量的存储段,从栈的临时区变换成.data段,延长了声明周期;
(2) static修饰全局变量,是为了限制全局变量的访问范围,让此全局变量变成本文件内可访问;
(3) static修饰函数,也是为了限制函数的访问范围,static函数本文件内可见;
(4) static修饰类成员变量,该变量归类所有,全体实例对象共享一份;
(5) static修饰类成员函数,该函数只能用来配置操作static类成员变量。

2.inline关键字解析

其实关于inline修饰符的使用往往要和宏联系在一起,比如如下的表达式的宏定义
#define ExpressionName(var1, var2) (var1+var2)*(var1-var2);
优点:这种宏定义形式的代码替换工作是在预编译阶段实现,没有参数压栈,代码生成等一系列操作,效率高,这是这种宏定义被使用的主要原因;

缺点:但是这种宏定义形式只是做了预处理器符号表中的简单替换,不能主动地进行参数有效性检测 ,也不能享受C++编译器严格的类型检查和提示WARNING的强大功能,并且宏表达式的返回值也不能被强制转换成可转换的合适的类型,正是基于这些考虑,推出inline修饰的内联函数,关于inline修饰符的具体使用,C99标准和GCC编译器额外扩展的定义存在一定区别,点击查看详情

  1. inline修饰的内联函数是在编译阶段,同文件内的调用点处不使用call process这种过程调用指令,而是直接将内联函数的汇编码直接填写在同文件内的调用处
  2. inline定义的类的内联函数,函数的代码被放入符号表中,在使用时是直接进行替换的(像宏一样展开),没有了调用的开销,效率高;
  3. inline可以作为某个类的成员函数修饰符,可以在函数体内部使用所在类的保护成员及私有成员。

总结来看:宏表达式的替换是字符串替换,发生在词法分析和预编译阶段;inline内联函数的替换是汇编代码级别的填充,发生在编译阶段。

3. volatile关键字解析

前面说到过编译器优化,有时会进行想当然的优化,比如会充分利用变量当前在寄存器中的缓存,但是因为编译器并无进程交互的概念,所以这种盲目的缓存读取优化可能会导致数据更新不同步的情况,所以用 volatile关键字来修饰一些进程间或多线程共享信息变量,声明对该变量的每次读取都需要从内存中提取最新的数据。一般用在以下几个地方:
a、并行设备的硬件寄存器(如:状态寄存器);
b、一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) ;
c、多线程应用中被几个任务共享的变量 。

总结来看:正如volatile的字面意思–“易挥发,不稳定的”,volatile修饰符使用的变量确实处于实时多变的状态。

4. const关键字解析

现在来看看最复杂的也是迷惑性最强的 const修饰符, const既可以修饰变量也可以修饰函数,先来看看修饰变量的情形:
1、以const修饰的常量值,具有不可变性,避免数字的直接使用,使用具有可读性的名称(const int MAX_SUM = 100;
2、C++编译器不为普通const常量分配空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有存储和读内存的操作,使得它效率更高;
3、 const修饰符是左结合的,故而考虑对于如下两种情况的分析:
int const *A; const绑定在int上,说明是修饰的int数值, 限定*A指向的内容不可变,指针A本身可变;
int * const A; const绑定在int* 指针上,限定指针A本身不可变,指针指向的内容可变

关于用于函数修饰的情形,可参考这篇文章。const修饰函数主要有三个方面可以修饰:
1. 形参修饰
(a) 值传递:因为是在栈上创建临时对象,过后即删,故而无需const保护,即不存在void Func(const int source)
(b) 指针传递: void Func(const char* source)便是规定函数内部不能对source指针指向的内容进行任何修改;
(c) 引用(别名)传递:如果传输对象size较大,创建临时对象不划算,采用引用传递,但是也面临着和指针传递一样的内容修改风险,void Func(const ArrayList &source)便是保证函数内部不能对source进行更改。

2. 返回值修饰:返回值的传递方式也是分为三种的
(a) 值传递返回值::函数内部会在栈上建立一个临时对象用来传递返回值,过后即删,同样无需const保护;
(b) 指针传递返回值:以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针,否则何来const保护作用的传承?

const char * GetString(void);
//如下语句将出现编译错误:
char*  str = GetString();
//正确的用法是
const char * str = GetString();

(c) 引用(别名)传递返回值:函数返回值采用“引用传递”的场合并不多,因为这是一直在同一个数据对象进行操作,一个数据对象如果存在较多的别名,并且还是不可控的,这是很可怕的,故而这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。

3. 类成员函数功能声明
使用在C++的类函数修饰场景,声明在函数中不改变类成员,如果在函数中存在更改类成员的操作,则编译器编译时会主动指出错误,所以这种用法是有点借助编译器自我约束自我提醒的意思,可提高程序的健壮性。借用

classStack
{
public:
   void Push(int elem);
   int Pop(void);
   int GetCount(void) const; // const 成员函数,
   /*
   **其实const 修饰的是this*对象指针,故而本质上和const修饰符的
   **this是隐含参数,const没法直接修饰它,就加在函数的后面了。const修饰*this是本质,
   **至于说“表示该成员函数不会修改类的数据。否则会编译报错”之类的说法只是一个现象,
   **根源就是因为*thisconst类型的
   */

private:
  int  m_num;
  int  m_data[10];
};

int Stack::GetCount(void)const
{
   m_num++; // 编译错误,在const成员函数中修改类成员m_num
   Pop();   // 编译错误,在const成员函数中调用非const成员函数,
   /*
   **非const函数的行为无法保证不修改类成员的,故而const成员函数不能调用非const成员函数
   **同样的道理,如果声明 const classStack demo;即声明该类的const对象,则该对象是不能
   **调用它的非const成员函数的,因为非const成员函数会修改类成员数据,这就不是const对象了
   **但是const对象持有的指针变量指向的内容是可以修改的,因为const对象只需要保证指针不变就可以
   **指针指向的数据变不变并不直接归属于const对象的const性质
   */
   return  m_num;
}

关于const成员函数的其他几点说明:

  1. const成员函数不可以修改类的任何成员数据(除了下面的特例), 编译器在编译阶段以该函数是否修改成员数据为依据,进行合法性判断;
  2. 加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数内部是可以mutable变量的。
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值