为什么C ++允许我将一个const char分配给一个const char *?!

令我惊讶的是,这汇编:

const char* c_str() { static const char nullchar = '\0'; return nullchar; } 

并在我的代码中引入了一个错误。 谢天谢地,我抓住了它。

这是故意的C ++,还是一个编译器错误? 是否有数据types被忽略的原因?
它工作在Visual C ++ 2010和GCC ,但我不明白为什么它应该工作,给出明显的数据types不匹配。 ( static也不是必须的)

正如你所定义的那样, nullchar是一个整数常量expression式,值为0。

C ++ 03标准定义了一个空指针常量,如下所示:“空指针常量是整数types的整数常量expression式(5.19)rvalue,其值为0。 长话短说,你的nullchar是一个空指针常量,这意味着它可以隐式转换,并分配给基本上任何指针。

请注意,所有这些元素都需要隐式转换才能工作。 例如,如果您使用'\1'而不是'\0' ,或者如果您没有nullchar指定const限定符, nullchar不会得到隐式转换 – 您的分配将失败。

包含这种转换是故意的,但广泛认为是不可取的。 0作为一个空指针常量是从Cinheritance的。我相当肯定Bjarne和其他大多数C ++标准委员会(以及大多数C ++社区)都非常喜欢去除这个特定的隐式转换,但是这样做会破坏与很多C代码的兼容性(可能接近所有的代码)。

这是一个古老的历史:它回到C.

C中没有null关键字。C中的空指针常量是:

  • 0L'\0' (记住char是一个整型), (2-4/2)
  • 例如(void*)0(void*)0L(void*)'\0'(void*)(2-4/2)

NULL macros (不是关键字!)扩展为这样的空指针常量。

在第一个C ++devise中,只有整型常量expression式被允许作为空指针常量。 最近std::nullptr_t被添加到C ++中。

在C ++中,而不是在C中,用整型常量expression式初始化的整型types的constvariables是一个整型常量expression式:

 const int c = 3; int i; switch(i) { case c: // valid C++ // but invalid C! } 

所以一个用expression式'\0'初始化的const char是一个空指针常量:

 int zero() { return 0; } void foo() { const char k0 = '\0', k1 = 1, c = zero(); int *pi; pi = k0; // OK (constant expression, value 0) pi = k1; // error (value 1) pi = c; // error (not a constant expression) } 

而你认为这不是完美的语言devise?


更新包括C99标准的相关部分…根据§6.6.6…

一个整数常量expression式应该有整数types,只能有整型常量,枚举常量,字符常量, sizeofexpression式的结果是整型常量,以及浮点常量是转换的立即操作数的操作数。 在整型常量expression式中转换运算符只能将算术types转换为整数types,除了作为sizeof运算符的操作数的一部分。

一些C ++程序员的澄清:

  • C使用术语“常量”来表示C ++程序员所知道的“文字”。
  • 在C ++中, sizeof总是一个编译时间常量; 但是C有可变长度数组,所以sizeof有时不是编译时间常量。

那么,我们看到§6.3.2.3.3状态…

值为0的整数常量expression式,或者转换为void *types的expression式称为空指针常量 。 如果一个空指针常量被转换为一个指针types,那么被调用的空指针所产生的指针将被保证与一个指向任何对象或函数的指针进行比较。


要查看这个function是多less年,请参阅C99标准中相同的镜像部分。

§6.6.6

一个整数常量expression式应该有整数types,只能有整型常量,枚举常量,字符常量, sizeofexpression式的结果是整型常量,以及浮点常量是转换的立即操作数的操作数。 在整型常量expression式中转换运算符只能将算术types转换为整数types,除了作为sizeof运算符的操作数的一部分。

§6.3.2.3.3

值为0的整数常量expression式,或者转换为void *types的expression式称为空指针常量 。 如果一个空指针常量被转换为一个指针types,那么被调用的空指针所产生的指针将被保证与一个指向任何对象或函数的指针进行比较。

nullchar是一个(编译时)常量expression式,值为0.所以这是公平的游戏隐式转换为空指针。

更详细地说:我在这里引用了1996年的标准草案 。

char是一个整数types。 nullchar是const,所以它是一个(编译时)整型常量expression式,如5.19.1节所示:

5.19常量expression式[expr.const]

1在一些地方,C ++需要求值为一个整数或枚举常量的expression式…一个整型常量expression式可能涉及到… constvariables…

此外,根据第4.10.1节, nullchar计算结果为0,允许将其隐式转换为指针:

4.10指针转换[conv.ptr]

1一个整数types的整型常量expression式( expr.const )rvalue的值为0(称为空指针常量)可以被转换为一个指针types。

也许一个直观的理由“为什么”这可能被允许(就在我的头顶上)是没有指定指针宽度,所以允许从任何大小的整型常量expression式转换为空指针。


更新了(较新的)C ++ 03标准的相关部分…根据§5.19.1…

一个整型常量expression式只能涉及用常量expression式(8.5)初始化的整型或枚举types的文本(2.13),枚举符, constvariables或静态数据成员,整型或枚举types的非types模板参数以及sizeofexpression式。

那么,我们期待§4.10.1…

空指针常量是整数types的整数常量expression式(5.19)右值,其值为零。 空指针常量可以转换为指针types; 结果是该types的空指针值,并且可以与指向对象的指针或指向函数types的指针的每个其他值区分开来。 相同types的两个空指针值应该相等。

它编译为这个编译的同样的原因

 const char *p = 0; // OK const int i = 0; double *q = i; // OK const short s = 0; long *r = s; // OK 

右边的expression式的types是intshort ,而被初始化的对象是一个指针。 这令你感到惊讶吗?

在C ++语言(以及C)中,值为0积分常量expression式(ICE)具有特殊的状态(尽pipeICE在C和C ++中定义不同)。 他们限定为空指针常量 。 当它们在指针上下文中使用时,它们被隐式转换为适当types的空指针。

typeschar是一个整型,在这个上下文中与int没有太大的区别,所以一个由0初始化的const char对象在C ++中也是一个空指针常量(但不是在C中)。

顺便说一句,C ++中的booltypes也是一个整型,这意味着一个由false初始化的const bool对象也是一个空指针常量

 const bool b = false; float *t = b; // OK 

它不会忽略数据types。 这不是一个错误。 它利用了你放在那里的const,看到它的值实际上是一个整数0(char是一个整数types)。

整数0是一个有效的(按定义)空指针常量,它可以转换为指针types(成为空指针)。

你为什么要空指针的原因是有一些指向“无处”的指针值,可以检查(即你可以比较一个空指针到一个整数0,你会得到真实的回报)。

如果你删除了const,你会得到一个错误。 如果你把double放在那里(就像很多其他的非整数types一样;我猜这个例外只是可以转换成const char * [通过重载转换运算符]的types),你会得到一个错误(甚至没有const)。 等等。

整个事情是,在这种情况下,你的实现看到你正在返回一个空ptr常量; 您可以将其转换为指针types。

这个问题的真正答案似乎已经在评论中结束了。 总结:

  • C ++标准允许整型的constvariables被认为是“整型常量expression式”。 为什么? 很可能绕过C只允许macros和枚举成为常量expression式的地方的问题。

  • (至less)可以追溯到C89,值为0的整型常量expression式可以隐式转换为(任何types的)空指针。 这经常用在C代码中,其中NULL经常是# #define as (void*)0

  • 回到K&R,字面值0被用来表示空指针。 这个惯例被广泛使用,代码如下:

     if ((ptr=malloc(...)) {...} else {/* error */} 

有一个汽车铸造。 如果你运行这个程序:

 #include <stdio.h> const char* c_str() { static const char nullchar = '\0'; return nullchar; } int main() { printf("%d" , sizeof(c_str())); return 0; } 

在我的电脑上输出很好 – >指针的大小。

编译器自动强制转换。 注意,至lessgcc给出警告(我不知道VS)

我认为这可能是types之间的空字符是常见的事实。 你在做的是当你返回空字符时设置一个空指针。 如果使用任何其他字符,则会失败,因为您没有将字符的地址传递给指针,而是将字符的值传递给指针。 Null是一个有效的指针和字符值,所以空字符可以被设置为指针。

简而言之,null可以被任何types用来设置一个空值,而不pipe它是一个数组,一个指针还是一个variables。