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