【C语言进阶深度学习记录】九 C语言中const的详细分析

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);           // 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编译器

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页