C语言限定符
C语言的限定符包括const、volatile、restrict
一、const
1、const修饰基础类型
例如:
const int a = 10;
a = 100;//此句在c语言中会报错,assignment of read-only variable 'a'
//a是一个只读变量
2、const修饰指针
const修饰指针有两种情况,一种是修饰指针,指不能改变指针的指向即常指针,数组名就是一个常指针,指向数组的第一个存储空间。
例如:
int a = 10, b = 20;
int* const p = &a;
p = &b;//此句报错,assignment of read-only variable 'p'
//p是一个只读变量
另一种情况是修饰指针指向的变量,指不能通过指针改变指针所指向的对象。
例如:
int a = 10, b = 20;
const int* p = &b;
p = &a;//此句可以正常运行,即可以改变指针指向
a = 20;//此句可以正常运行,即可以通过变量名来修改本身的值
*p = 30;//此句报错,assignment of read-only location '* p'
//不可通过指针改变指针所指向的对象
但是以上都是将int与连在一起表示,如果将const写在int与****中间呢?我们来看下面代码
int a = 10, b=20;
int const * p = &a;
p = &b;
*p = 30;//此句报错,[Error] assignment of read-only location '* p'
//不可通过指针改变指针所指向的对象
可见int const * p与const int* p等价。const在谁前面就修饰谁,这只是一种记忆方法。
int* const p = &a;//在这里,const 位于p前面,即修饰p,p是一个常指针
const int* p = &b;//在这里,const 位于int*前面,它的类型是指向一个具有const限定符的int类型的指针
int const * p;//在这里,const位于*前面, *并不是一个类型,故把const提前变成const int* p
题外话1:
查看<string.h>头文件会发现里面的函数声明在传入一个固定的字符串时几乎都加了const修饰,这是为了不在函数中不小心的修改指针所指向的内容。
题外话2:
在以下语句中
char *cp;
const char *ccp;
ccp = cp;
cp = ccp;//这是非法的
这是合法的,并且在大部分的字符串库函数中都是这么使用的。他符合约束条件:两个操作数都是指针有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符也就是说无限定符的操作数可以给有限定符的操作数赋值。
左操作数是一个指向有const限定符的char的指针;
右操作数是一个指向没有限定符的char的指针;
char类型与char类型是相容的,左操作数所指向的类型具有右操作数所指向类型的限定符(无),再加上自身的个限定符(const)。(左操作数的限定程度要大于等于右操作数)相反的话若cp = cpp则会报错。
来看以下语句
foo(const char **p){}
int main(int argc,const char **argv){
foo(argv);
}
//编译报错:argument is incompatible with prototype
//参数与原型不匹配
这是为什么呢?首先我们要了解const char argv。constchar argv是一个没有限定符的指针类型。他的类型是指向有const限定符的char类型的指针的指针。char ****** p是指向**char ***,const char **p 指向的是const char *。**不符合约束条件:每个实参都应该具有自己的类型,这样它的值就可以赋值给与它所对应的形参类型的对象(该对象的类型不能包含有限定符)。
char *p;
const char *q;
q = p;//实际上这个代码是可行的,也就是说char* 和const char* 是相容的,但是这并不意味着char** 和const char**可以相容
二、volatile
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。volatile 提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有 volatile 关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。所以遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
例如在stm32GPIO寄存器中每个寄存器被设置为IO类型而IO类型是由宏定义实现的#define __IO volatile