C语言中的const关键字表示这个变量的值在其整个作用域中都保持不变。
但const是在语义层面的检查,是编译器在编译期间,用语法检测来保证数据没有被修改。但在运行时,const修饰的变量和其他未被const修饰的普通变量一样是放在内存中的数据区。必要时可以通过修改内存来达到修改值的目的。
void main(){
int a = 0;
a = 1;
}
一个变量有两个维度来描述,一个是地址,另一个是这个地址上的值。上面这段代码,我们在变量a的地址上修改了它的值,由0修改为了1。
void main(){
const int a = 0;
a = 1; //编译时不允许通过
}
上面的代码结果是显而易见的,编译无法通过。因为代码虽然没有尝试修改a的地址,但修改了a的值。
既然const关键字限制了变量值的修改,那变量的地址呢?
void main(){
int a = 0;
&a = &a; // 编译器不允许通过
}
变量的地址本身是无法修改的,因为同样不被编译器允许,即使这个地址就是它自身的地址。
既然const修饰的普通变量值和地址都无法修改,那数组变量值修改呢?我们先来看没有const修饰的数组。
void main(){
int a[] = { 0, 0 };
a[0] = 1;
a[1] = 1;
}
对于数组也需要分为两个维度,一个是数组的首地址,另一个是数组的值。对数组来说,数组名表示的是数组的首地址,而数组的值和数组的数据类型有关,但最终也表示数值。
void main(){
const int a[] = { 0, 0 };
a[0] = 1; // 编译时不允许通过
a[1] = 1; // 编译时不允许通过
}
上面的数组用const修饰后,结果也很明显,编译无法通过。因为我们无法修改数组的值。
而对于数组的地址,自然也是无法编译通过。
void main(){
int a[] = { 0, 0 };
int b[] = { 1, 1 };
a = b; // 编译无法通过
}
因为数组名代表的就是数组的首地址,“a = b”这条语句相当于把数组b的地址赋值给了数组a 的地址。而普通变量代表的是这个地址上的值,普通变量名的赋值操作是值对值,而数组名的赋值操作时地址对地址。
void main(){
int* a = 0;
int b[] = { 1, 1 };
a = b; // a的值为数组b的地址
}
由于编译器的原因,这其中的细节被隐藏掉了,导致看起来不太习惯。这也是我们对数组初始化只有用for循环或内存操作memset()的原因。
说回const的话题。如果数组存放的是指针呢?能否修改其中的值?
void main(){
const int* a[] = { 0 };
int b = 0;
a[0] = &b;
}
上面的代码是可以通过编译的,最终a[0]的值为整型变量b的地址。但如果尝试修改b的值呢?
void main(){
const int* a[] = { 0 };
int b = 0;
a[0] = &b;
*a[0] = 1; // 编译器不允许通过
}
上面这段代码编译器不允许通过,因为我们尝试修改了指向整型变量的地址上的值。我们回到普通的变量来看针对指针变量的情况。
首先我们要知道,变量的地址本身是无法修改的,因为同样不被编译器允许。
void main(){
int a = 0;
int b = 0;
&a = &b; // 编译器不允许通过
}
既然const修饰的普通变量的值我们无法修改,那指针变量呢?
void main(){
const int a = 0;
int b = 0;
a = &b; // 编译器不允许通过
}
上面这段代码很清晰,const修饰的整型变量a的值不允许修改,因此将b的地址赋值给a的操作是不被允许的,编译器自然无法通过。
但如果a是指向整型变量的指针类型呢?例如下面这段代码。
void main(){
const int* a = 0;
int b = 0;
a = &b;
}
很奇怪的是,编译器通过了。指针变量a表示的是,这个变量存放着指向整型变量的地址。但这个地址的值是允许改变的。
那什么情况下不允许改变呢?
void main(){
const int* a = 0;
int b = 0;
a = &b;
*a = 1; // 编译器不允许通过
}
当我们尝试去修改指针变量a所指向的地址的值时,这个情况是不允许的。上面的代码相当于尝试修改变量b的值,但被编译器拒绝了。
当没有const修饰时,b的值可以被正确修改。
void main(){
int* a = 0;
int b = 0;
a = &b;
*a = 1; // 变量b的值被修改为了1
}
由此看来。const并不是完全限制当前变量的值的修改。
而是,当const修饰的变量是普通变量时,不允许修改其中的值。当const修饰的是指针变量时,不允许修改这个指针指向的地址上的值。
而针对多级指针,也是如此。
void main(){
const int** a = 0; // 二级指针
int* b = 0;
int c = 0;
a = &b;
b = &c;
** a = 1; // 编译不允许通过
}
上面二级指针变量a指向了b,b指向了c。如果没有const修饰,最终变量c的值为1。但当有const修饰时,编译器不允许代码修改最终的变量的值。