程序笔试题-const变量通过指针修改问题 2012-10-06 20:45:18
分类: C/C++
const的变量在特定情况下可以通过指针修改,但是在另一些情况下是不能通过指针修改。
以下是VC6下才测试。
1 不能修改的情况
#include <stdio.h>
int const a = 10;
void main(void)
{
int *p = (int*)&a;
*p = 20;
printf("%d ", *p);
}
int const a = 10;
void main(void)
{
int *p = (int*)&a;
*p = 20;
printf("%d ", *p);
}
程序编译通过,但运行时错误:
指示a存储的空间不可以写,也就是没有写权限,不能修改其值。估计是存储在全局空间,且只有可读属性。
2 能修改的情况
#include <stdio.h>
void main(void)
{
int const a = 10;
int *p = (int*)&a;
*p = 20;
printf("&a=%d ", &a);
printf(" p=%d ", p);
printf(" a=%d ", a);
printf("*p=%d ", *p);
}
void main(void)
{
int const a = 10;
int *p = (int*)&a;
*p = 20;
printf("&a=%d ", &a);
printf(" p=%d ", p);
printf(" a=%d ", a);
printf("*p=%d ", *p);
}
程序能正常运行,且常量被修改了,但是有一个问题:
为什么 printf(" a=%d
", a);
打印a=10?
难道一个地址空间可以存储不同的俩个值,当然不能,哈哈,这是因为a是const变量,编译器对a在预处理的时候就进行了替换。编译器只对const变量的值读取一次。所以打印的是10。a实际存储的值发生了改变。但是为什么能改变呢,从其存储地址可以看出来,其存储在堆栈中。
验证如下:
#include <stdio.h>
void main(void)
{
int const a = 10;
int b = 20;
int *p = (int*)&a;
*p = 20;
printf("&a=%x ", &a);
printf("&b=%x ", &b);
printf(" p=%x ", p);
printf(" a=%d ", a);
printf("*p=%d ", *p);
}
void main(void)
{
int const a = 10;
int b = 20;
int *p = (int*)&a;
*p = 20;
printf("&a=%x ", &a);
printf("&b=%x ", &b);
printf(" p=%x ", p);
printf(" a=%d ", a);
printf("*p=%d ", *p);
}
变量a和b的地址相近。
总结,const全局变量存储在全局存储空间,其值只有可读属性,不能修改;
const局部变量存储在堆栈中,可通过指针修改其值;
const变量在预处理是处理,编译器只对其值读取一次。
知乎上回答
const在C语言中是表示道义上保证变量的值不会被修改,并不能实际阻止修改,通过指针可以修改常变量的值,但是会出现一些不可知的结果。几种情况不同,我们一个一个来看。
1、直接赋值
const int a = 3;
a = 5;
// const.c:6:2: error: assignment of read-only variable ‘a’
这种情况不用多说,编译错。
2、使用指针赋值,@孙健波提到的方法,在gcc中的warning,g++中出现error,是因为代码写得不对,由非const的变成const不用显式的转换,const变为非const需要显式转换,这种情况应当使用显式的类型转换。
const int a = 3;
int* b = (int*) &a;
printf("a = %d, *b = %d
", a, *b);
*b = 5;
printf("a = %d, *b = %d
", a, *b);
运行结果(注:使用msvc编译的结果一致):
$ gcc const.c
$ ./a.out
a = 3, *b = 3
a = 5, *b = 5
$ g++ const.cpp
$ ./a.out
a = 3, *b = 3
a = 3, *b = 5
这里使用g++编译时,a的值之所以没有改变,是因为编译时a是常量,然后被编译器编译为立即数了。因此对使用b指针修改不会更改a的值。
值得注意的是,如果a被定义为全局常变量,使用指针修改会引发segment fault。
在函数的原型中,我们也常用const修饰指针,表示函数的实现者在道义上不会去修改这个指针所指向的空间。例如我们熟知的strcpy函数,原型如下:
char* strcpy(char* dst, const char* src);
传入的参数src类型是const char*,表示函数内部实现不会修改src所指向的空间。之所以说是道义上,是因为在内部通过上述指针强制类型转换的方式可以修改该空间的值。另外,如果我们声明了我们不会修改传入指针的所指向的空间,那么我们也不应当去修改这块空间,因为这个传入的指针可能会是一个不可写的内存,然后出现段错误。