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变量不修改内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言中的const和free用法详解

    注意:C语言中的const和C++中的const是有区别的,而且在使用VS编译测试的时候.如果是C的话,请一定要建立一个后缀为C的文件,不要是CPP的文件.因为,两个编译器会有差别的. 一.C语言中的const比较常见的用法,const做常量 #include<stdio.h> #include<malloc.h> #include<string.h> /* C中的const用法(使用VS测试的时候,要注意建立一个C后缀的文件,因为C的编译器和C++的编译器还是有区别的

  • 详解C语言中const关键字的用法

    关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变,我想一定有人有这样的疑问,C语言中不是有#define吗,干嘛还要用const呢,我想事物的存在一定有它自己的道理,所以说const的存在一定有它的合理性,与预编译指令相比,const修饰符有以下的优点: 1.预编译指令只是对值进行简单的替换,不能进行类型检查 2.可以保护被修饰的东西,防止意外修改,增强程序的健壮性 3.编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编

  • C语言在头文件中定义const变量详解

    C语言在头文件中定义const变量详解 在头文件中定义const不会有多变量的警告或错误,如果该头文件被大量包含会造成rom空间的浪费. 通过查看*.i文件的展开呢,可以发现每个.i文件都会有相应的变量展开. 查看*.map文件,能查看到该变量的多个地址分配. 在预编译的时候如果在头文件定义了const变量,每一个包含该头文件的c文件都会将其展开,而在编译的时候不会报错,因为这符合语法规则,每一个包含这个头文件的*.c文件都会编译一次这个变量,分配一个新的地址,然后在链接的时候也不会报错,因为每

  • 解析C语言中如何正确使用const

    基本解释 const是一个C语言的关键字,它限定一个变量不允许被改变.使用const在一定程度上可以提高程序的健壮性,另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一些帮助.虽然这听起来很简单,但实际上,const的使用也是c语言中一个比较微妙的地方,微妙在何处呢?请看下面几个问题. 问题: const变量 & 常量为什么我象下面的例子一样用一个const变量来初始化数组,ANSI C的编译器会报告一个错误呢?const int n = 5;int a[n]; 答

  • 总结C语言中const关键字的使用

    什么是const? 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的.(当然,我们可以偷梁换柱进行更新:) 为什么引入const? const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点. const关键字使用非常的灵活,这一点和php差别很大,php中const用来在类中定义一个常量,而在c中,const因位置不同有不同的作用,因情景不同有不同的角色,使用起来也是非常的灵活. (1):const用来修饰普通的变量(指针变量除外)的时

  • C语言中const与指针使用方法总结

    C语言中const与指针使用方法总结 在这里分享一下自己的心得,希望和大家一起分享技术,如果有什么不足,还请大家指正.写出这篇目的,就是希望大家一起成长,我也相信技术之间没有高低,只有互补,只有分享,才能使彼此更加成长. 总结: * const 值不能改变,指向可改变 const * 值能改变,指向不可改变 const * const 都不能改变 实例代码: #include <stdio.h> int main(int argc, const char * argv[]) { // 1 可改

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

    这小段文章要理清楚的是,在C语言中,const是如何保证变量不被修改的? 我们可以想到两种方式: 第一种,由编译器来阻止修改const变量的语句,让这种程序不能通过编译: 第二种,由操作系统来阻止,即把const 的内存地址访问权限标记为"只读",一旦运行中的程序试图修改它,就会产生异常,终止进程. 上面想到的这两种方式,都能达到让某一变量的值不被修改的目的,那么究竟是哪一种呢?我们写两个例子来看一看. 先来看一个简单的例子,源文件const.c: #include <stdio

  • 深入了解C语言中的const和指针

    目录 前言 指针的赋值 问题 ANSI C 有关简单赋值的标准 问题解决 const修饰 const修饰变量 const修饰指针 前言 文章内容由阅读<C专家编程>整理而来.希望可以帮助大家解决在指针赋值和const方面的问题,也希望大家多多指正文章中的错误,共同进步. 指针的赋值 问题 将一个类型为 char** 的值赋值给一个 const char** 类型的对象是否合法呢? 先说结果,在vs的环境下,编译器不会报错也不会有任何警告. 但在linux环境下用gcc编译就会出现下面的警告:

  • 深入聊聊C语言中的Const关键字

    目录 前言 01const简述 02常量的应用 常量作为函数的参数 C++中应用加const 03#define和const 总结 前言 const是一个C语言的关键字,它限定一个变量不允许被改变.使用const在一定程序上可以提高程序的健壮性,另外,在观看别人代码的时候,清晰理解const所起的作用,对理解别人的程序有所帮助. 01const简述 下面简单描述一下const,基本都是教科书的知识.const修饰的变量,其值存放在只读数据段中,其值不能被改变.称为只读变量.关于什么是数据段,什么

  • C语言中const和C++中的const 区别详解

    C语言中const和C++中的const 区别详解 C++的const和C语言的#define都可以用来定义常量,二者是有区别的,const是有数据类型的常量,而宏常量没有,编译器可以对前者进行静态类型安全检查,对后者仅是字符替换,没有类型安全检查. 而C语言中的const与C++也有很大的不同,在C语言中用const修饰的变量仍是一个变量,表示这个变量是只读的,不可显示地更改,而在C++中用const修饰过后,就变成常量了.例如下面的代码: const int n=10; int a[n];

  • c语言和c++语言中const修饰的变量区别浅析

    目录 c: 修饰全局变量: 修饰局部变量: c++: 修饰全局变量: 修饰局部变量: 总结: 在c语言中: 在c++语言中: 总结 c: 修饰全局变量: 用const修饰的全局变量是没有办法直接修改的,间接的修改也是不成功的(语法可以通过,但是编译运行的时候会报错.)(const只要是修饰全局变量,那么就会储存到常量区中,收到常量区的保护.) 修饰局部变量: 但是如果用const修饰局部变量,同样的也是没有办法直接修改的,但是是可以间接修改的. int main() { const int b

  • 详解C++中的const关键字及与C语言中const的区别

    const对象默认为文件的局部变量,与其他变量不同,除非特别说明,在全局作用域的const变量时定义该对象的文件局部变量.此变量只存在于那个文件中中,不能别其他文件访问.要是const变量能在其他文件中访问,必须显示的指定extern(c中也是)   当你只在定义该const常量的文件中使用该常量时,c++不给你的const常量分配空间--这也是c++的一种优化措施,没有必要浪费内存空间来存储一个常量,此时const int c = 0:相当于#define c 0:    当在当前文件之外使用

  • C语言中bool变量的深入理解

    目录 前言 bool类型变量的大小 bool 值与0比较 c语言中bool如何输出 总结 前言 在一些高级语言当中,为了能够完成更好的逻辑判断,因此就有了bool类型,bool类型的变量值只有true和false两种. 而在C语言中,一般认为0为假,非0为真. 这是因为c99之前,c90是没有bool类型的的.但是c99引入了_Bool类型(_Bool就是一个类型,不过在新增头文件stdbool.h中,被重新用宏写成了 bool,为了保证C/C++兼容性). 目前为止大部分C语言书籍采用的标准还

  • C语言中const,指针和引用的关系

    目录 const 与指针 const 与引用 const 与指针.引用 总结 const 与指针 我们写一段代码来探究以下 int a = 10, b = 20; int* p1 = &a; *p1 = 100; p1 = &b; const int* p2 = &a; int const* p3 = &a; int* const p4 = &a; const int* const p5 = &a; 在上面 const int* p2;与int const*

随机推荐