C90增加了两个属性:不变性和易变性.
通过关键字const 和 volatile 声明的, 这样就创建了受限类型(qualified type).
C99 增加了第三个限定词restrict, 用以方便编译器优化。
类型限定词 const
如果变量声明中带有关键字const,则不能通过赋值、增量或减量来修改该变量的值。
const int nochange; /* constant */ nochange = 12 /* false */
可以初始化一个const 变量, 初始化之后,不可以再改变它。
const int nochange = 12 // true const int days1[3] = {31,30,31}; // true
在指针和参量声明中使用const
特别注意区分: 指针本身是const 与 指针指向的值是const
const 紧挨着的是什么,什么就是不变的
const int days1[3] = {31,30,31}; // true 相当于 const int *days1 ; const在 * 左边, 使得数据成为常量
const float *pf ; // const 紧挨着 float ,说明 float常量是不变的
float *const pt ; // const紧挨着 指针pt,说明是一个常量指针
float const *pt; // const紧挨着 *pt, 相当于解引用,是float 常量,不是指针,注意区分
后面有更好的理解方法!!!
1. 指针指向的值是const
const float *pf ; // pf 指向一个常量
特别注意: const放置在不同的位置,有不同的含义,注意区分!!!
float const *pfc; // 等同于 const float *pfc
把const放在 类型名 的后边和 * 的前边,意味着指针不能够用来改变它所指向的值。
总之:下面理解起来比较容易
一个位于 *左边 任意位置的 const 使得 数据成为常量
而 一个位于 * 右边 的const使得 指针自身成为常量 。
但是pf本身的值是可以改变的(本身是个地址),它可以指向另一个const值。
eg:
const float *pf_new;
pfc = pf_new;
2. 指针本身是const (指针本身的值不可变)
eg:
float * const pt; // pt 是一个常量指针
必须总是指向同一个地址,但所指向的值可以改变。
3. 地址是常量,地址指向的内容也是常量
const float * const ptr;
意味着ptr必须总是指向同一个位置(地址),并且它所指位置存储的值也不能改变。
4. 常见用法
常见用法是声明作为函数形式参量的指针。
eg:
一个名为display()的函数显示一个数组的内容。 为了使用它,可以把数组名作为参数传送,但数组名是一个地址,
这样做将允许函数改变调用函数中的数据。下面的原型防止了这样的情况发生。
void display(const int array[], int limit);
在函数原型和函数头部,参量声明 const int array[] 与 const int *array, 该声明表明array指向的数据是不可变的。
5. 对全局数据使用const
文件中共享const 全局常量
策略一:不使用 .h 文件 , 在一个文件中进行定义声明,在下一个文件中进行引用声明!
// file1.c 定义一些全局变量 const double PI = 3.14159; const char *MONTH[3] = { "january","february","march" };
// file2.c 使用其他文件中定义的全局变量 #include<stdio.h> extern double PI; extern char *MONTH[]; // 引用声明 int main() { printf("%f ", PI); printf("%s ", MONTH[1]); printf("%s ", MONTH[2]); return 0; }
策略二:使用 .h 文件 , 将常量放在一个include文件中。这时还必须使用静态外部存储类
// 将常量放在一个include文件中。这时还必须使用静态外部存储类 #ifndef _CONSTANT_H #define _CONSTANT_H static const double PI = 3.14159; static const char *MONTH[3] = { "january","february","march" }; #endif
// file1.c 使用在其他文件定义的全局变量 #include<stdio.h> #include "constant.h" int file1_print(); int file1_print() { printf("%f ", PI); return 0; }
// file2.c 使用在其他文件定义的全局变量 #include<stdio.h> #include "constant.h" int main() { printf("%f ", PI); printf("%s ", MONTH[1]); printf("%s ", MONTH[2]); int a = file1_print(); return 0; }
static是必不可少的,通过使每个标识符成为静态外部的,实际上给了每个文件一个独立的数据拷贝。
缺点: 复制了数据,如果常量数据包含着巨大的数组,可能是一个问题。头文件尽量不要定义全局变量
策略三:项目中常见的使用方法
.h 头文件extern 声明该变量,在 .c源文件中定义这个变量
其他 .c 文件如果要调用这个变量,直接包含头文件即可。
//constant.h 声明 #ifndef _CONSTANT_H #define _CONSTANT_H extern double PI; extern char *MONTH[]; #endif
// constant.c 定义 #include "constant.h" double PI = 3.14159; char *MONTH[3] = { "january","february","march" };
// file1.c 调用 #include<stdio.h> #include "constant.h" int file1_print(); int file1_print() { printf("%f ", PI); printf("%s ", MONTH[0]); printf("%s ", MONTH[2]); return 0; }
// file2.c调用 #include<stdio.h> #include "constant.h" int main() { printf("%f ", PI); printf("%s ", MONTH[1]); printf("%s ", MONTH[2]); int a = file1_print(); return 0; }
6. const 指针变量间的赋值关系 常量指针 指针常量 傻傻分不清楚!!!
https://www.cnblogs.com/witty/archive/2012/04/06/2435311.html
一) 常量指针。
常量是形容词,指针是名词,以指针为中心的一个偏正结构短语。这样看,常量指针本质是指针,常量修饰它,表示这个指针乃是一个指向常量的指针(变量)。
指针指向的对象是常量,那么这个对象不能被更改。
在C/C++中,常量指针是这样声明的:
1)const int *p;
2)int const *p;
常量指针的使用要注意,指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改,也就是说常量指针可以被赋值为变量的地址,之所以叫做常量指针,是限制了通过这个指针修改变量的值。例如:
int a = 5;
const int b = 8;
const int *c = &a; // 这是合法的,非法的是对c的使用
*c = 6; // 非法,但可以这样修改c指向的对象的值:a = 6;
const int *d = &b; // b是常量,d可以指向b,d被赋值为b的地址是合法的
细心的朋友在使用字符串处理函数的时候,应该会注意到这些函数的声明。它们的参数一般声明为常量指针。例如,字符串比较函数的声明是这样的:
int strcmp(const char *str1, const char *str2);
可是这个函数却可以接收非常量字符串。例如这段程序:
char *str1, *str2;
str1 = "abcde1234";
str2 = "bcde";
if(strcmp(str1, str2) == 0)
{
printf("str1 equals str2.");
}
str1和str2的内容显然是可以更改的,例如可以使用“str1[0] = x;”这样的语句把str1的内容由“abcde1234”变为“xbcde1234”。因为函数的参数声明用了常量指针的形式,就保证了在函数内部,那 个常量不被更改。也就是说,对str1和str2的内容更改的操作在函数内部是不被允许的。(就目前的应用来看,我觉得设置常量指针就是为函数参数声明准 备的,不然还真不知道用在什么地方呢,呵呵!)
虽然常量指针指向的对象不能变化,可是因为常量指针是一个变量,因此,常量指针可以不被赋初始值,且可以被重新赋值。例如:
const int a = 12;
const int b = 15;
const int *c = &a; // 为了简化代码,很多人习惯赋初始值
const int *d;
d = &a; // 这样当然是可以的
c = &b; // 虽然c已经被赋予初始值,可是仍然可以指向另一个变量
特点是,const的位置在指针声明运算符*的左侧。只要const位于*的左侧,无论它在类型名的左边或右边,都声明了一个指向常量的指针,叫做常量指针。
可以这么想,*左侧是常量,指针指向的对象是常量。