概述
const常用来修饰普通变量,指针变量,甚至是函数的返回值,可以提高程序的健壮性,其用的最多之处是用来修饰函数的形参,防止修改了调用函数中实参指向地址中的数据及在被调函数运行中修改了形参值。
本文主要讨论C语言中const的使用规则及其含义,下文由浅入深展开讨论。
基本规则:
规则1、const修饰后的变量,变成只读(read-only),称只读(read-only)变量或const变量;
规则2、只读变量在声明时必须同时初始化,之后变成只读,不可再修改;
规则3、const放在类型前与类型后效果是等价的;
规则4、const变量初始化时,可以将同类型普通变量赋给它;
规则5、普通非指针变量,可以将同类型const变量赋给它;
规则6、指向普通类型的指针,是不可以将指向const修饰类型的指针赋给它。
总的原则:
不要尝试去改变const修饰的量的值
(编译报错还好,万一不报错,如通过取const变量地址后修改或者是强制类型转换后修改,程序的运行可能会出现难以想象的错误)。
修饰普通变量
int b = 100;
int const a = 100;
等价于
const int a = 100;
说明:a是一个整型变量,被const修饰,声明时必须同时初始化,之后不要尝试修改a的值,尤其是通过指针。
初始化的值可以是普通常量,也可以是普通变量,当然同类型的const变量更是可以的。
const int c[] = {1,2,3};
说明:数组的元素类型是const int,即c[0] c[1] c[2]均被const修饰,不要尝试修改。
修饰一级指针
形式1:
int* const p = &b;
说明: p是一个指向int型的指针,被const修饰,声明时必须同时初始化, 之后不要尝试修改p的值。
形式2:
const int* p;
p = &a;
等价于
int const* p;
p = &a;
说明:其实这里p就是一个普通指针,并没有被const修饰,即可以修改p的值。但p指向的是const int*,即指向的是一个被const修饰的int,
也就是*p是被const修饰,不要尝试修改*p的值;
p+i与p是同类型(i在这里是个整数),显然*(p+i)指向的也是const int变量,也是不能修改它的值。
形式3:
const int* const p = &a;
说明:形式3就是形式1与形式2的合并,p及*p均被const修饰;声明时必须同时初始化。
之后不要尝试修改p与*p;
类型转换解读
规则4、5、6主要是涉及到一个类型的转换。
int a;
const int ca = a; //规则4 const变量初始化时,可以将同类型普通变量赋给它;
int* pa;
int* const pca = pa; //pca是const变量,指向的是int*,符合规则4
a = ca; //规则5 普通非指针变量,可以将同类型const变量赋给它
const int* cpa; //cpa不是const变量,是一个普通指针变量,只是它指向的是一个const型
pa = cpa; /* 编译报错,根据规则6、指向普通类型的指针,是不可以将 指向const修饰类型的指针赋给它。原因很简单,这样赋值成功的话,
*pa将会改变*cpa的值,而*cpa是指向const int,只读的 */
cpa = pa;//编译通过,即int*是可以转换为const int*的。
char *p = "abc"; //大多数编译器会产生告警或报错,普通指针指向了不可改的常量区;
const char *p = "abc";//编译通过
char arr[] = "abc";//编译通过,会给数组arr分配4个字节,存放'a''b''c''\0',
小结:
通过以上的分析:
const修饰普通变量:const int <=> int const,其实也就是一种。
const修饰指针变量:
const int * <=> int const*
int* const
const int* const <=> int const* const
其实就三种。
形式 | const | 可赋值给 | 不可赋值给 |
---|---|---|---|
int a | 无 | const int | |
const int a | 修饰a,a是只读的 | int | |
int* pa | 无 | int* const const int* const int* const | |
int* const pca | 修饰指针本身pca,pca只读 | int* const int* const int* const | |
const int* cpa | 修饰指针指向值*cpa,*cpa只读 | const int* const | int* int* const |
const int* const cpca | 修饰cpca 修饰*cpca 二者都是只读 | const int* | int* int* const |
修饰形参
C语言知识点:C语言中函数的形参是实参的一份拷贝。
形式1:
int func(const int a);
说明:
形参:a被const修饰,在func内部,不能修改a的值。
实参:int型,有无const修饰均可。
形式2:
int func(const int *p);
说明:
形参:是指针p, 其指向的是const int型,即指向被const修饰的int型变量。func内部不能修改*p和*(p+i)的值,可以修改p的值。
实参:形参是指针,实参也要是指针,且其指向及本身有无const修饰均可。
形式3:
int func(int* const p);
说明:
形参:是指针p,被const修饰,其指向的是int型。func内部不能修改p的值,但可以修改*p和*(p+i)的值。
实参:形参是指针,实参也要是指针,且其指向及本身有无const修饰均可。
形式4:
int func(const int* const p);
说明:
形参:是指针p,被const修饰,同时*p也被const修饰。func内部不能修改p的值,也可以修改*p和*(p+i)的值。
实参:形参是指针,实参也要是指针,且其指向及本身有无const修饰均可。
形式5:
int func(const int arr[]);
等价于形式2
int func(const int *p);
说明:C语言的知识点,一维数组作形参,其实就是相当于一个指向元素类型的指针。
高级篇:修饰二级指针
const int** pp; <=> int const** pp; //规则3
说明: const 是用于修饰**pp, 即**pp不可修改
int*const* pp
说明:const是用于修饰*pp, 即*pp不可修改
int ** const pp
说明: const用于修饰pp, 即pp不可修改,是个const变量,声明时必须初始化
来个终极大魔王:
const int*const* const pp
说明:pp *pp **pp 均被const修饰,pp是个const变量,声明时必须同时初始化。
关于char**不能赋值给const char**却可赋值const char *const*的问题探究:
假如通过强制类型转换char**pp0赋给了const char **pp1, *pp1也等于*pp0,同时由于*pp1未被const修饰,可以重新赋值,可以赋const char*,赋值的同时相当于也赋给了*pp0这个,此时虽然**pp1是只读不能修改,但是**pp0却能修改,能通过**pp0将const char给改了,显然不行的。倘若*pp1也被const修饰(const char *const*),那么,*pp1不可以重新赋值,那么自然也不出会修改const char。
int main()
{
const char* s = "abc";//*s是只读的
char* p = NULL;
char** pp0 = &p;
const char** pp1 = pp0;//假设赋值能过
*pp1 = s; //*pp1也就是*pp0,也就是普通指针p,p也会指向s
*p = 'b'; //通过*p,或**pp0就可以修改掉s中的值,显然不合理
return 0;
}
如果pp1的类型是const char *const* pp1; 那么*pp1 = s;这条语句就不通,即不能给常量重新赋值。