const和指针的问题

一个bug引起的

最近北京那边同事碰到了一个问题,找到我时说是已经搞了四五天, 说是软件发布在即十分紧急啥的。 出问题的代码大概是这个样子:

1
2
3
4
5
6
#define SERVER_NAME "www.foo.com"

void main() {
...
gethostbyname(SERVER_NAME);
}

这段代码就是要通过DNS查找主句IP地址,如果成功的话会有一个callback。 问题是一直都无法正确的获得IP地址。

const 和指针的一些说明

一个很流行的面试题就是const int * p 和 int * const p的区别。 其实const int * 和 int const * 是一样的, 就是一个指向const int的指针。 而 int * const p表示一个指向int的const 指针。

那么问题来了,有人会问可以通过指针来修改const 变量的值吗?

1
2
3
4
5
6
7
int main() {
const int a = 1;
int * p = (int *)&a;
*p = 2;
printf("%d %d\n", a, *p);
return 0;
}

上面的代码将没有用const int * 的指针, 而是用int * 的指针来强制改变a的值。 这段代码的结果是什么呢? 其实这个要看编译器。

对于gcc 结果会是

1
2 2

即const 变量a的值可以被改变。

但如果用g++编译, 结果就是

1
1 2

其实如果去看汇编的代码,会发现其实a的值还是被改变了,所以 * p的值会是2. 但C++编译器会把a的值直接替换为1, 而不是去a的地址取值, 所以a的值是1.

但如果a是一个全局变量 或是静态变量 ,这段代码运行时会segmentation fault, 因为这时a是在只读的区域的,修改只读区域会使程序出错。

而前面的例子中a是一个局部变量,是在栈中分配的,所以是可以被修改的。

问题的原因

对于开始提到的那个bug, SERVER_NAME其实是一个字符串的指针,并且是在只读区,而我同事封装的这个函数gethostbyname()的参数需要是一个可读写的地址(因为一些底层硬件上的原因)。

而偏偏这个程序所在的平台,虽然不可以通过指针去修改全局const变量 ,但程序不会crash。 所以同事可能没有去考虑这方面的问题。

改成这样就一切正常了。 虽然不同编译器的行为有些不同, 但编程时最好不要通过指针去修改const的变量 。

1
2
3
4
5
void main() {
char server_name[] = "www.foo.com";
...
gethostbyname(server_name);
}