C 和 C++ 允许把指向 "某物" 的指针赋值给指向 "不可改变" 的 "某物" 的指针, 如
char* call = "fsck";
char const* call_const = call;
而且, 如果确定不会更改所知向的对象, 那么推荐加上 const
, 这是 C++ 的代码编写建议之一. 但是, 要把指向 “某物” 的二级指针赋值给... 见鬼, 简单的说, 下面这种赋值, 任何一个负责任的 C++ 编译器会报出指针不兼容错误:
char* call = "fsck";
char** ptr_to_call = &call;
char const** ptr_const_to_call = ptr_to_call; // error
C/C++ 这样做, 目的其实是在于防范将 char const*
赋值给 char*
的意外行为. 这样说也许很难理解, 那么请看下面这段示例代码
char const* CALL = "fsck";
void bind(char const** m)
{
*m = CALL;
}
int main(void)
{
char* s;
bind((char const**)&s); // explicit convert
s[1] = 'u'; // crash on write
return 0;
}
在调用 bind
时, 采取强制转换来规避编译错误. 而此时调用 bind
就出现了问题, 它把 char const*
类型的 CALL
变量赋值给了实际上是 char*
的 s
, 结果就是在紧接下来的那个赋值中, 程序由于尝试去写只读内存区而崩溃. 有崩溃有真相, 这就是对标题的解答.
另一个类似的例子是, 子类对象的二级指针不能赋值给父类对象的二级指针, 如
struct base {};
struct inherit : base {};
void f(void)
{
inherit** i = NULL;
base** b = i; // error
}