为什么C++允许我将const char赋给const char*?!

Meh*_*dad 70 c++ type-safety implicit-conversion

令我惊讶的是,这编译:

const char* c_str()
{
    static const char nullchar = '\0';
    return nullchar;
}
Run Code Online (Sandbox Code Playgroud)

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

这是故意的C++,还是编译器错误?是否有理由主动忽略数据类型?
它适用于Visual C++ 2010和GCC,但我不明白为什么它应该工作,因为明显的数据类型不匹配.(static也没必要.)

Jer*_*fin 69

正如您所定义的那样,nullchar是一个整数常量表达式,其值为0.

C++ 03标准将空指针常量定义为:"空指针常量是整数类型的整数常量表达式(5.19)rvalue,其计算结果为零." 总而言之,你nullchar是一个空指针常量,这意味着它可以被隐式转换并分配给基本上任何指针.

请注意,隐式转换需要所有这些元素才能工作.例如,如果您使用的是'\1'代替'\0',或者如果您没有指定const限定符nullchar,则不会得到隐式转换 - 您的赋值将失败.

包含这种转换是有意的,但众所周知是不合需要的.0作为一个空指针常量从C继承.我相当肯定Bjarne和C++标准委员会的大多数其余部分(以及大多数C++社区)都非常喜欢删除这个特定的隐式转换,但这样做会破坏与很多C代码的兼容性(可能接近所有代码).

  • 值得补充的是,在C中,值为"0"的`const`变量的质量不是空指针常量,这意味着OP的代码在C中无效(并且因此不会被任何"破坏")变化,因为从C的角度来看已经破了). (4认同)
  • 所以也许是这样的:为什么允许整数常量表达式被隐式转换为空指针(而不是只允许文字)?(请注意,这是一个假设的问题). (2认同)
  • @Mehrdad:在C或C++中没有这样的转换.在C++中,它通过隐式转换为`bool`来工作.在C中,它通过与文字"0"的隐式比较来工作,即`if(p)`等同于`if(p!= 0)`.后者也不使用转换为`int`. (2认同)

cur*_*guy 28

这是一段古老的历史:它可以追溯到C.

nullC中没有关键字.C中的空指针常量是:

  • 具有0值,积分常量表达式等0,0L,'\0'(记住,char是整体型),(2-4/2)
  • 这样的表达转换为void*,例如(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!
}
Run Code Online (Sandbox Code Playgroud)

所以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)
}
Run Code Online (Sandbox Code Playgroud)

你认为这不是语音设计的合理吗?


更新以包含C99标准的相关部分......根据§6.6.6...

一个整数常量表达式应具有整数型和应仅具有是积分常数,枚举常数,字符常数,操作数sizeof 表达式,其结果是积分常数,和浮动常量是铸件的立即操作数.整数常量表达式中的转换运算符只能将算术类型转换为整数类型,除非作为sizeof 运算符的操作数的一部分.

对C++的一些澄清 - 只有程序员:

  • 对于C++程序员所知的"文字",C使用术语"常量".
  • 在C++中,sizeof始终是编译时常量; 但C具有可变长度数组,因此sizeof有时不是编译时常量.

然后,我们看到§6.3.2.3.3状态......

值为0的整型常量表达式或此类表达式转换为类型 void *称为空指针常量.如果将空指针常量转换为指针类型,则保证将结果指针(称为空指针)与指向任何对象或函数的指针进行比较.


要查看此功能的使用年限,请参阅C99标准中相同的镜像部件...

§6.6.6

一个整数常量表达式应具有整数型和应仅具有是积分常数,枚举常数,字符常数,操作数sizeof表达式,其结果是积分常数,和浮动常量是铸件的立即操作数.整数常量表达式中的转换运算符只能将算术类型转换为整数类型,除非作为sizeof运算符的操作数的一部分.

§6.3.2.3.3

值为0的整型常量表达式或此类表达式转换为类型 void *称为空指针常量.如果将空指针常量转换为指针类型,则保证将结果指针(称为空指针)与指向任何对象或函数的指针进行比较.

  • +1好消息,谢谢.关于你的问题:不,我不认为这是合理的语言设计,因为它显然在我的代码中引入了一个不必要的bug.:P (3认同)

Man*_*agu 14

nullchar 是一个(编译时)常量表达式,值为0.因此,隐式转换为空指针是公平的游戏.

更详细:我引用了1996年的标准草案.

char是一个完整的类型. nullchar是const,所以它是一个(编译时)积分常量表达式,如5.19.1节所述:

5.19常量表达式[expr.const]

1在几个地方,C++需要表达式来计算一个整数或枚举常量......一个整数常量表达式可能涉及...... const变量......

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

4.10指针转换[conv.ptr]

1 整数类型的整数常量表达式(expr.const)rvalue,其求值为零(称为空指针常量)可以转换为指针类型.

也许直观的原因"为什么"这可能是允许的(只是在我的头顶)是指针宽度没有指定,因此允许从任何大小的整数常量表达式转换为空指针.


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

一个积分常数表达式可以只涉及文字(2.13),统计员,const变量或常量表达式(8.5),积分或枚举类型的非类型模板参数,并初始化积分或枚举类型的静态数据成员sizeof表达式.

然后,我们期待§4.10.1......

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

  • 我也很好奇.我想知道C++标准是否真的允许这样做,或者过早地进行持续替换的副作用. (2认同)

AnT*_*AnT 11

它编译的原因与编译完全相同

const char *p = 0; // OK

const int i = 0;
double *q = i; // OK

const short s = 0;
long *r = s; // OK
Run Code Online (Sandbox Code Playgroud)

在右边的表达式有类型intshort,而被初始化的对象是一个指针.这让你感到惊讶吗?

在C++语言(以及C语言)中,具有值的整数常量表达式(ICE)0具有特殊状态(尽管ICE在C和C++中的定义不同).它们有资格作为空指针常量.当它们在指针上下文中使用时,它们被隐式转换为适当类型的空指针.

Type char是一个整数类型,与int此上下文没有太大区别,因此const char初始化的对象0在C++中也是一个空指针常量(但不在C中).

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

const bool b = false;
float *t = b; // OK
Run Code Online (Sandbox Code Playgroud)

后来针对C++ 11的缺陷报告改变了空指针常量的定义.在更正之后,空指针常量只能是"值为零的整数文字或类型为std :: nullptr_t的prvalue".在校正之后,上述指针初始化在C++ 11中不再格式良好.


Ped*_*ira 6

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

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

你想要空指针的原因是有一些"指向无处"的指针值并且可以检查(即你可以将空指针与整数0进行比较,然后你会得到回报).

如果删除const,则会出错.如果你把double放在那里(就像许多其他非整数类型一样;我想异常只是可以转换为const char*的类型[通过转换运算符的重载]),你会得到一个错误(甚至没有const).等等.

整个问题是,在这种情况下,您的实现会看到您返回一个null ptr常量; 你可以转换为指针类型.


Man*_*agu 5

似乎这个问题的许多真正答案最终都出现在评论中.总结一下:

  • C++标准允许const将整数类型的变量视为"整数常量表达式".为什么?很可能绕过C只允许宏和枚举来保持整数常量表达式的问题.

  • 在(至少)回到C89之前,具有值0的整数常量表达式可隐式地转换为(任何类型的)空指针.这也是C代码,其中经常使用的NULL是经常#define倒是作为(void*)0.

  • 回到K&R,文字值0已用于表示空指针.此约定遍布各处,代码如下:

    if ((ptr=malloc(...)) {...} else {/* error */}
    
    Run Code Online (Sandbox Code Playgroud)