1 const的分析
不管是C语言还是C++语言,const都是一个非常重要的关键字。今天这篇文章着重学习记录C语言中的const。C++语言中稍有不同。
在C语言中:
- const修饰的变量是只读的,本质上还是变量。注意,不是常量。
- const修饰的变量如果是局部非静态变量,则该变量存储在栈上
- const修饰的变量如果是全局变量,则该变量一定是存储在全局的数据区(.data)
- const修饰的变量,在程序编译期就得出了相应的属性,在运行期间该const关键字没有作用
注意:const修饰的变量,不是真正的常量。它只是告诉编译器它修饰的变量不能出现在赋值符号的左边。后面我们会知道,const修饰的局部变量是可以通过指针改变的,但是const修饰的全局变量是不可以改变的(当然,这是现代C编译器,标准C编译器编译的,还是可以修改。后面会有说明)
在现代C语言编译器中,修改const修饰的变量会导致程序段错误。程序崩溃。因为现代C语言编译器会将const修饰的变量储存在只读存储区(.rodata)。
但是在标准C语言编译器中,不会将const修饰的变量储存在只读存储区。而是储存在可修改的全局数据区(.data),所以它的值依然可以改变。只要通过指针找到它的地址,就可以改变它的值。
- 比如下面的代码,放到比较老的BCC编译器中编译与在vs2017中编译效果肯定是不同的。由于没有安***CC编译器且代码逻辑比较简单,实验部分,读者可以自己做实验:
#include <stdio.h>
const int g_cc = 2;
int main()
{
const int cc = 1;
int* p = (int*)&cc;
printf("cc = %d\n", cc);
*p = 3;
printf("cc = %d\n", cc);
p = (int*)&g_cc;
printf("g_cc = %d\n", g_cc);
*p = 4;
printf("g_cc = %d\n", g_cc);
return 0;
}
2 const本质的分析实验
其实上面的内容就已经说了const的本质。
- C语言中的const使得变量具有只读属性
- 现代C语言编译器中,将const修饰的具有全局生命周期的变量(包括全局变量和局部静态变量)储存在.rodata区。
const 不能定义真正意义上的常量
2.1 代码案例分析
- 9-2-lyy.c
#include <stdio.h>
const int g_array[5] = {0};
void modify(int* p, int v)
{
*p = v;
}
int main()
{
int const i = 0;
const static int j = 0;
int const array[5] = {0};
modify((int*)&i, 1); // ok
// modify((int*)&j, 2); // error
modify((int*)&array[0], 3); // ok
// modify((int*)&g_array[0], 4); // error
printf("i = %d\n", i);
printf("j = %d\n", j);
printf("array[0] = %d\n", array[0]);
printf("g_array[0] = %d\n", g_array[0]);
return 0;
}
- 在现代C语言编译器中进行编译运行吗,上述代码是可以运行成功的。证明了局部const变量是可以通过指针修改的。
- 同时上述代码的第17和19行,如果不注释掉,程序的就无法运行,会产生段错误。这也说明了全局变量和局部静态变量如果被const修饰的话,编译器会将其修饰的变量放到.rodata区,这个区的内容是不允许被改变的,通过指针也无法改变。
3 const修饰函数参数和返回值时的情况
- const修饰函数参数,表示在函数体内不希望修改该参数的值
- const修饰函数的返回值,表示返回值不可改变。多用于函数返回值为指针的情形。
注意:C语言中的字符串字面量,存储于.rodata区,在程序中需要使用const char* 指针修饰字符串。
如:
3.1 代码案例分析
- 代码9-3-lyy.c
#include <stdio.h>
const char* f(const int i){
//i = 5;
return "Lyy+Learn_C";
}
int main(){
char* pc = f(0);
printf("pc = %s\n", pc);
pc[3] = '_';
printf("pc = %s\n", pc);
return 0;
}
- 上述代码编译后(编译有警告),运行会产生错误,因为pc所指向的内存为.rodata区。不能修改.rodata区里面的内容。第16行会导致程序产生段错误。
- 当然,第5行注释的代码,会导致程序编译出错。
4 总结
- const使得变量具有只读属性
- const不能定义真正意义上的常量
- const将具有全局生命周期的变量(包括const全局变量,const局部静态变量),储存在具有只读属性的.rodata区中。不能通过指针法修改该区域中的变量
- 标准C编译器与现代C编译器稍有不同,标准C编译器不会将const修饰的具有全局生命周期的变量储存在.rodata区中,所以可以通过指针修改该变量。
一般来说,我们使用的都是现代C编译器