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

时间:2012-08-19 06:31:42

标签: c++ implicit-conversion type-safety

令我惊讶的是,这编译:

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

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

这是故意的C ++还是编译器错误?是否有理由忽略数据类型?
它在Visual C ++ 2010和GCC中有效,但我不明白为什么它应该工作,因为明显的数据类型不匹配。 (static也不是必需的。)

8 个答案:

答案 0 :(得分:69)

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

C ++ 03标准将空指针常量定义为:“空指针常量是整数类型的整数常量表达式(5.19)rvalue,其值为: 为了简而言之,你的nullchar是一个空指针常量,这意味着它可以被隐式转换并分配给任何指针。

请注意,隐式转换需要所有这些元素才能工作。例如,如果您使用'\1'代替'\0',或者如果const指定了nullchar限定符,那么您就不会得到隐式转换 - 你的任务失败了。

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

答案 1 :(得分:28)

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

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

  • 值为0的整数常量表达式,如00L'\0'(请记住char是整数类型),(2-4/2)
  • 此类表达式转换为void*,例如(void*)0(void*)0L(void*)'\0'(void*)(2-4/2)

NULL (不是关键字!)扩展为此类空指针常量。

在第一个C ++设计中,只允许使用整型常量表达式作为空指针常量。最近{C}已添加std::nullptr_t

在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 ++的一些澄清 - 只有程序员:

  • C使用术语“常量”来表示C ++程序员所知道的“文字”。
  • 在C ++中,sizeof始终是编译时常量;但是C具有可变长度数组,因此sizeof有时编译时常量。

然后,我们看到§6.3.2.3.3陈述......

  

一个整数常量表达式,其值为0,或者这样的表达式强制转换为类型    void * ,称为空指针常量。如果将空指针常量转换为指针类型,则保证将结果指针(称为空指针)与不等于指向任何对象或函数的指针进行比较。


要查看此功能的年龄,请参阅C99 standard中相同的镜像部分...

§6.6.6

  

整数常量表达式应具有整数类型,并且只能具有整数常量的操作数,枚举常量,字符常量, sizeof 表达式,其结果为整数常量和浮动常量,它们是强制转换的直接操作数。整数常量表达式中的转换运算符只能将算术类型转换为整数类型,除非作为 sizeof 运算符的操作数的一部分。

§6.3.2.3.3

  

一个整数常量表达式,其值为0,或者这样的表达式强制转换为类型    void * ,称为空指针常量。如果将空指针常量转换为指针类型,则保证将结果指针(称为空指针)与不等于指向任何对象或函数的指针进行比较。

答案 2 :(得分:14)

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

更详细:我在这里引用1996 draft standard

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

  

5.19常量表达式[expr.const]

     

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

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

  

4.10指针转换[conv.ptr]

     

1整数类型的整数常量表达式( expr.const )rvalue    计算结果为零(称为空指针常量)可以是    转换为指针类型。

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


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

  

整数常量表达式只能涉及文字(2.13),枚举数,const变量或用常量表达式(8.5)初始化的整数或枚举类型的静态数据成员,非输入整数或枚举类型的模板参数,以及sizeof表达式。

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

  

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

答案 3 :(得分:11)

它编译的原因与编译

的原因相同
const char *p = 0; // OK

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

const short s = 0;
long *r = s; // OK

右侧的表达式具有类型intshort,而正在初始化的对象是指针。这会让你感到惊讶吗?

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

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

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

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

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

答案 4 :(得分:6)

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

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

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

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

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

答案 5 :(得分:5)

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

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

  • 至少可以追溯到C89,具有值0的整数常量表达式可以隐式转换为(任何类型的)空指针。这在C代码中经常使用,其中NULL经常#define(void*)0

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

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

答案 6 :(得分:2)

有一个自动演员。如果你运行这个程序:

#include <stdio.h>
const char* c_str()
{
    static const char nullchar = '\0';
    return nullchar;
}

int main()
{
    printf("%d" , sizeof(c_str()));
    return 0;
}

我的计算机上的输出结果为4 - &gt;指针的大小。

编译器自动转换。注意,至少gcc发出警告(我不知道VS)

答案 7 :(得分:2)

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

简而言之,null可以被任何类型用来设置空值,无论它是数组,指针还是变量。