令我惊讶的是,它编译为:
const char* c_str()
{
static const char nullchar = '\0';
return nullchar;
}
它在我的代码中引入了一个错误。谢天谢地,我抓住了它。
这是 C++ 故意的,还是编译器错误?主动忽略数据类型是否有原因?
它在 Visual C++ 2010 和GCC中工作,但我不明白为什么它应该工作,因为明显的数据类型不匹配。(static
也没有必要。)
令我惊讶的是,它编译为:
const char* c_str()
{
static const char nullchar = '\0';
return nullchar;
}
它在我的代码中引入了一个错误。谢天谢地,我抓住了它。
这是 C++ 故意的,还是编译器错误?主动忽略数据类型是否有原因?
它在 Visual C++ 2010 和GCC中工作,但我不明白为什么它应该工作,因为明显的数据类型不匹配。(static
也没有必要。)
正如您所定义的,它nullchar
是一个值为 0 的整数常量表达式。
C++03 标准将空指针常量定义为:“空指针常量是整数类型的整数常量表达式 (5.19) 右值,其计算结果为零。” 长话短说,你nullchar
是一个空指针常量,这意味着它可以被隐式转换并分配给基本上任何指针。
请注意,所有这些元素都是隐式转换工作所必需的。例如,如果您使用了'\1'
而不是'\0'
,或者您没有指定const
限定符 for nullchar
,您将不会获得隐式转换——您的赋值将失败。
包含这种转换是有意的,但众所周知是不可取的。0 作为一个空指针常量是从 C 继承的。我很确定 Bjarne 和 C++ 标准委员会的其他大多数成员(以及一般 C++ 社区的大多数成员)都非常喜欢删除这个特定的隐式转换,但是这样做会破坏与许多 C 代码的兼容性(可能接近全部)。
这是一个古老的历史:它可以追溯到 C。
C中没有null
关键字。C 中的空指针常量是:
0
, 0L
, '\0'
(记住这char
是一个整型),(2-4/2)
void*
, like (void*)0
, (void*)0L
, (void*)'\0'
,(void*)(2-4/2)
NULL
宏(不是关键字!)扩展为这样的空指针常量。
在第一个 C++ 设计中,只允许整数常量表达式作为空指针常量。最近std::nullptr_t
被添加到 C++ 中。
在 C++ 中,但不是在 C 中,const
用整型常量表达式初始化的整型变量是整型常量表达式:
const int c = 3;
int i;
switch(i) {
case c: // valid C++
// but invalid C!
}
所以const char
用表达式初始化的'\0'
是一个空指针常量:
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)
}
你认为这不是健全的语言设计吗?
更新以包括 C99 标准的相关部分...根据 §6.6.6...
整数常量表达式应具有整数类型,并且只能具有整数常量、枚举常量、字符常量、
sizeof
结果为整数常量的表达式和作为强制转换的直接操作数的浮点常量的操作数。整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,但作为运算sizeof
符操作数的一部分除外。
对纯 C++ 程序员的一些说明:
sizeof
始终是编译时间常数;但是 C 有可变长度数组,所以sizeof
有时不是编译时间常数。然后,我们看到 §6.3.2.3.3 状态......
值为 0 的整数常量表达式,或转换为 type 的此类表达式
void *
称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与指向任何对象或函数的指针不相等。
要了解此功能的历史,请参阅C99 标准中相同的镜像部分......
§6.6.6
整数常量表达式应具有整数类型,并且只能具有整数常量、枚举常量、字符常量、
sizeof
结果为整数常量的表达式和作为强制转换的直接操作数的浮点常量的操作数。整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,但作为运算sizeof
符操作数的一部分除外。
§6.3.2.3.3
值为 0 的整数常量表达式,或转换为 type 的此类表达式
void *
称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与指向任何对象或函数的指针不相等。
nullchar
是一个(编译时)常量表达式,值为 0。因此,隐式转换为空指针是公平的游戏。
更详细地说:我在这里引用了1996 年的标准草案。
char
是一个整数类型。 nullchar
是 const,所以它是一个(编译时)整数常量表达式,根据第 5.19.1 节:
5.19 常量表达式 [expr.const]
1 在几个地方,C++ 需要计算为整数或枚举常量的表达式……一个整数常量表达式可能涉及……常量变量……
此外,nullchar
根据第 4.10.1 节,计算结果为 0,允许将其隐式转换为指针:
4.10 指针转换 [conv.ptr]
1 整数类型的整数常量表达式 ( expr.const ) 右值(称为空指针常量)可以转换为指针类型。
也许允许“为什么”这样做的一个直观原因(就在我的脑海中)是没有指定指针宽度,因此允许从任何大小的整数常量表达式转换为空指针。
更新了(较新的)C++03 标准的相关部分...根据 §5.19.1...
一个整数常量表达式只能包含字面量 (2.13)、枚举数、
const
变量或整数或枚举类型的静态数据成员,这些成员使用常量表达式 (8.5)、整数或枚举类型的非类型模板参数和sizeof
表达式进行初始化。
然后,我们查看§4.10.1 ...
空指针常量是整数类型的整数常量表达式 (5.19) 右值,其计算结果为零。空指针常量可以转换为指针类型;结果是该类型的空指针值,并且可以与指向对象的指针或指向函数类型的指针的所有其他值区分开来。相同类型的两个空指针值应比较相等。
它编译的原因与编译的原因相同
const char *p = 0; // OK
const int i = 0;
double *q = i; // OK
const short s = 0;
long *r = s; // OK
右边的表达式有 and 类型int
,short
而被初始化的对象是一个指针。这让你感到惊讶吗?
在 C++ 语言(以及 C)中,具有值的整型常量表达式 (ICE)0
具有特殊状态(尽管 ICE 在 C 和 C++ 中的定义不同)。它们有资格作为空指针常量。当它们在指针上下文中使用时,它们被隐式转换为适当类型的空指针。
typechar
是一个整型,int
和这个上下文没有太大区别,所以一个const char
由 初始化的对象0
在 C++ 中也是一个空指针常量(但在 C 中不是)。
BTW,bool
C++ 中的 type 也是整数类型,这意味着const bool
初始化的对象false
也是空指针常量
const bool b = false;
float *t = b; // OK
后来针对 C++11 的缺陷报告更改了空指针常量的定义。更正后,空指针常量只能是“值为零的整数文字或 std::nullptr_t 类型的纯右值”。上述指针初始化在更正后在 C++11 中不再是格式良好的。
它没有忽略数据类型。这不是一个错误。它利用了你放在那里的 const 并看到它的值实际上是一个整数 0 (char 是一个整数类型)。
整数 0 是一个有效的(根据定义)空指针常量,可以转换为指针类型(成为空指针)。
你想要空指针的原因是有一些指针值“指向无处”并且可以检查(即你可以将空指针与整数0进行比较,你会得到真实的回报)。
如果你去掉 const,你会得到一个错误。如果你把 double 放在那里(与许多其他非整数类型一样;我猜例外只是可以转换为 const char* [通过转换运算符的重载]的类型),你会得到一个错误(即使 w/o常量)。等等。
整个事情是,在这种情况下,您的实现看到您正在返回一个空 ptr 常量;您可以将其转换为指针类型。
似乎这个问题的很多真正答案都在评论中结束了。总结一下:
C++ 标准允许const
将整数类型的变量视为“整数常量表达式”。为什么?很可能绕过 C 只允许宏和枚举代替整型常量表达式的问题。
(至少)追溯到 C89,值为 0 的整型常量表达式可以隐式转换为(任何类型的)空指针。这在 C 代码中经常使用,其中NULL
经常#define
'd 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;
}
我的电脑上的输出是 4 -> 指针的大小。
编译器自动转换。注意,至少 gcc 会发出警告(我不知道 VS)
我认为这可能是空字符在类型之间很常见的事实。您正在做的是在返回空字符时设置一个空指针。如果使用任何其他字符,这将失败,因为您没有将字符的地址传递给指针,而是将字符的值传递给指针。Null 是一个有效的指针和字符值,因此可以将空字符设置为指针。
简而言之,任何类型都可以使用 null 来设置空值,无论它是数组、指针还是变量。