文章目录
- 1 const的分析
- 2 const本质的分析实验
- 2.1 代码案例分析
- 3 const修饰函数参数和返回值时的情况
- 3.1 代码案例分析
- 4 总结
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中编译效果肯定是不同的。由于没有安装BCC编译器且代码逻辑比较简单,实验部分,读者可以自己做实验:
#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); // errormodify((int*)&array[0], 3); // ok// modify((int*)&g_array[0], 4); // errorprintf("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编译器