C语言中的const如何保证变量不被修改

 更新时间:2021年04月07日 09:27:37   作者:Xilaii  
这篇文章主要给大家介绍了关于C语言中const如何保证变量不被修改的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

这小段文章要理清楚的是,在C语言中,const是如何保证变量不被修改的?

我们可以想到两种方式:

第一种,由编译器来阻止修改const变量的语句,让这种程序不能通过编译;

第二种,由操作系统来阻止,即把const 的内存地址访问权限标记为“只读”,一旦运行中的程序试图修改它,就会产生异常,终止进程。

上面想到的这两种方式,都能达到让某一变量的值不被修改的目的,那么究竟是哪一种呢?我们写两个例子来看一看。

先来看一个简单的例子,源文件const.c:

#include <stdio.h>
const int a=10;
int main()
{
 int *p=&a;
 printf("initial: %d\n",a);
 *p=1;
 printf("modified: %d\n",a);
 return 0;
}

编译,会收到一个 warning:

$ gcc -o const1 const1.c
const.c: In function ‘main':
const.c:7:12: warning: initialization discards ‘const' qualifier from pointer target type [-Wdiscarded-qualifiers]
     int *p=&a;

忽略之,运行程序:

$ ./const1
initial: 10
Segmentation fault (core dumped)

运行出错了,报错是“segmentation fault”,即“段错误”,它是在提醒我们,程序中用错误的权限访问了内存某区域。这说明,操作系统把变量$a$加载到了一段只读内存区域之中,因此对该区域地址的写操作将引发异常,这是由操作系统的内存保护机制决定的。

也就是说,在这段程序里,const的只读属性是由操作系统来实现的,而不是由编译器来实现的(编译器只抛出了warning,并没有阻止编译通过)。

这对吗?不完全对,我们来看另一个例子,源文件const2.c:

#include <stdio.h>
int main()
{
 const int a=10;
 int *p=&a;
 printf("initial: %d\n",a);
 *p=1;
 printf("modified: %d\n",a);
 return 0;
}

编译,还是收到同样的warning:

$ gcc -o const2 const2.c
const.c: In function ‘main':
const.c:6:12: warning: initialization discards ‘const' qualifier from pointer target type [-Wdiscarded-qualifiers]
     int *p=&a;

忽略之,运行程序:

./const2
initial: 10
modified: 1

咦?怎么成功运行了,而且a的值还被顺利修改了?

结合以上两个例子,我们可以得出以下推测:

const只是C语言中的一种对变量的修饰符,例子中的a,与其说是“常量”,不如说是“不打算修改的变量”。它只是语法上的一种声明,它的作用就是告诉编译器“我不想修改它”,因此编译器会从语法上检查程序中是否有修改它的语句(例如“a=1;”),一旦发现这种“违背初衷”的语句,就会报错阻止你。

然而,编译器所阻止的仅仅是对a这个符号对应值的修改而已,却并不阻止对这个地址的值的修改,源文件“const2.c”之所以能顺利通过编译且正常运行,就是因为它利用一个名字不叫a的指针指向它,从而绕过了编译器的语法检查。

打个比方,周树人的笔名叫鲁迅,警察只知道要抓鲁迅,这时候他就可以用一句“你们抓鲁迅跟我周树人有什么关系?”来骗过他们。

从这个角度来说,const的作用是靠编译器仅仅从语法检查来实现的,因此存在运行时的漏洞。

那么为什么“const1.c”就不能正常运行呢?

仔细看这两个源程序,区别仅仅在于,在“const1.c”中,a被声明为全局变量,而在“const2.c”中,它被声明为main函数中的一个局部变量。全局变量与局部变量的区别在于,前者会在程序开始运行之前就被加载,加载后会一直留在内存中,且加载的位置在数据区,直到程序退出;后者只有在运行到它时才会被加载,且加载的位置是运行时的栈帧,一旦超出作用于就会被回收。

因此,编译器会对被声明为全局变量的const int a进行优化,把它放到只读内存区内,这一内存区的权限是“read\ only”,权限信息由操作系统所维护的段表来保存,程序每访问某地址时,操作系统都会检测其访问权限是否合法。“const2.c”中企图用“写”的方式来访问“只读”的段,自然会报出“segment fault"的错了。

从这个角度来说,当a是全局变量时,编译器把原本只是“不打算修改的变量”优化成了“真正的常量”,然后交给操作系统去维持其不变属性。

综上所述,C的初衷只是让编译器去保证$const$的不变属性,这一属性有漏洞(可以用指针去骗过编译器修改它),所以当const修饰的对象是全局变量时(全局变量很重要,因为很多源文件都要访问它,牵一发而动全身,所以不应轻易更改),编译器知道自己的能力有限,只能管得了编译,管不了运行时如何,所以优化了语句把它编程真正的常量,让操作系统的内存保护功能来履行这一职责。

这一优化,并不是C规定的,而是编译器厂商出于实际应用的考虑作出的选择。

以上,是我根据编译器和程序运行时的行为所做的推测,这一思路并不妥当,只是我在编程时遇到了上述两个例子的困惑,又没找到说得很清楚的资料,所以就写出来了,若要进一步验证,应该查看编译后的可执行文件分段情况,我偷了个懒没看,暂时放在这里。

如果推测不正确,希望有前辈指出。

总结

到此这篇关于C语言中const如何保证变量不被修改的文章就介绍到这了,更多相关C语言const变量不修改内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++封装静态链接库和使用的详细步骤

    C++封装静态链接库和使用的详细步骤

    这篇文章主要介绍了C++封装静态链接库和使用,本文描述了怎么去把一个C++程序封装成一个静态库并且如何去使用这些静态库,需要的朋友可以参考下
    2022-08-08
  • C++超详细分析讲解内联函数

    C++超详细分析讲解内联函数

    为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数
    2022-06-06
  • C++超详细讲解内存空间分配与this指针

    C++超详细讲解内存空间分配与this指针

    this 指针在C++类和对象中是个很方便实用的关键字,可以简化对象成员属性的调用,使代码表达的含义更加准确;在之前的学习中我们都可以判断变量所占内存空间大小,那么我们创建的类对象所占的内存空间怎么计算呢?想知道this的妙用和类对象占用的内存空间就来跟我学习吧
    2022-05-05
  • C语言实现共享单车管理系统

    C语言实现共享单车管理系统

    这篇文章主要为大家详细介绍了C语言实现共享单车管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • C++ 多线程之互斥量(mutex)详解

    C++ 多线程之互斥量(mutex)详解

    这篇文章主要为大家详细介绍了C++多线程之互斥量(mutex),文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • C语言基于graphics.h实现圣诞树

    C语言基于graphics.h实现圣诞树

    这篇文章主要介绍了圣诞树代码,c语言编程,基于graphics.h实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • C语言之初始if语句详解

    C语言之初始if语句详解

    本文主要介绍C语言中的if语句,这里详细介绍了if 语句并提供了简单的示例代码,希望能帮助编程入门的小伙伴学习,希望能够给你带来帮助
    2021-08-08
  • VS2022中使用Copilot的图文教程

    VS2022中使用Copilot的图文教程

    大家都知道Copilot可以自动帮助写代码,那么这个工具是如果使用的呢?很多朋友不是很清楚,今天小编给大家分享一篇教程关于VS2022中使用Copilot的图文教程,感兴趣的朋友一起看看吧
    2022-04-04
  • 给ActiveX签名的实现方法详解

    给ActiveX签名的实现方法详解

    本篇文章是对给ActiveX签名的实现方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++实现简单的计算器小功能

    C++实现简单的计算器小功能

    这篇文章主要为大家详细介绍了C++实现简单的计算器小功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02

最新评论