C++ 不支持的 C 元素( 二 )


10. C 语言允许在不声明任何对象的声明中使用无意义的存储类说明符:
static struct S { int i; };这在 C++ 中是不允许的 。
此外,您会注意到在 C 语言中,typedef 在形式上也只是存储类说明符之一,它允许您创建没有声明别名的无意义的 typedef 声明:
typedef struct S { int i; };C++ 不允许这样的 typedef 声明 。
公平地说,C 中的此类声明并非完全没有意义:它们仍然声明了 struct S 类型 。
11. C 语言允许在声明中显式重复 cv 限定符:
const const const int a = 42;从 C++ 的角度来看,代码是不正确的 。(C++ 也对类似的过度限定视而不见,但只能通过中间类型名称:typedef 名称,典型的模板参数) 。
12.在C中,直接复制volatile对象是没有问题的(至少从形式代码正确性的角度来看):
void foo(void){struct S { int i; };volatile struct S v = { 0 };struct S s = v;s = v;}在 C++ 中,隐式生成的复制构造函数和赋值运算符不将 volatile 对象作为参数 。
13、在C语言中,任何值为0的整型常量表达式都可以作为空指针常量:
void *p = 2 - 2;void *q = -0;在采用 C++11 标准之前,C++ 也是如此 。然而,在现代 C++ 中,整型值中,只有文字空值可以充当空指针常量,更复杂的表达式不再有效 。从 C++ 的角度来看,上述初始化是不正确的 。
14. C 不支持右值的 cv 限定 。特别是,函数返回值的 cv 限定会立即被语言忽略 。连同数组到指针的自动转换,这允许您绕过一些常量正确性规则:
struct S { int a[10]; };const struct S foo(){struct S s;return s;}int main(){int *p = foo().a;}然而,值得注意的是,尝试在 C 中修改右值会导致未定义的行为 。
从 C++ 的角度来看,foo() 的返回值以及数组 foo().a 保留了 const 限定,并且不可能将 foo().a 隐式转换为类型 int * 。
15. [C23] C 预处理器不熟悉 true 和 false 等字面量 。在 C 中,true 和 false 仅作为标准头文件 <stdbool.h> 中定义的宏可用 。如果没有定义这些宏,那么根据预处理器的规则,#if true 和#if false 都应该表现得像#if 0 。
同时,C++ 预处理器必须自然地识别 true 和 false 文字,并且它的 #if 指令必须以“预期”的方式处理这些文字 。
当 C 代码不包含 <stdbool.h> 时,这可能是不兼容的来源:
#if trueint a[-1];#endif这段代码在C++中显然是不正确的,但同时在C中却很容易编译 。
16. 从 C++11 开始,C++ 预处理器不再将 <literal><identifier> 序列视为独立的标记 。从 C++ 语言的角度来看,这种情况下的 <identifier> 是一个文字后缀 。为了避免这种解释,在 C++ 中,这些标记应该用空格分隔:
#define D "d"int a = 42;printf("%"D, a);printf 的这种格式对于 C 是正确的,但从 C++ 的角度来看是不正确的 。
17. main函数的递归调用在C中是允许的,但在C++中是不允许的 。C++程序一般不允许以任何方式使用main函数 。
18. 在 C 中,字符串文字是 char [N] 类型,而在 C++ 中它们是 const char [N] 。即使“旧”C++ 支持将字符串文字转换为类型 char * 作为异常,此异常仅在直接应用于字符串文字时才有效
char *p = &"abcd"[0];从 C++ 的角度来看,这样的初始化是不正确的 。
19. 在 C 中,声明为 int 类型但未明确指示有符号或无符号的位字段可以是有符号或无符号(这是实现定义的) 。在 C++ 中,这样的位域总是有符号的 。
20、在C语言中,typedef类型名和struct类型标签在不同的命名空间,互不冲突 。例如,这样一组声明从 C 的角度来看是正确的:
struct A { int a; };typedef struct B { int b; } A;typedef struct C { int c; } C;在 C++ 中,类类型没有单独的标记概念:类名与 typedef 名称共享相同的命名空间,并且可能与它们冲突 。为了与 C 代码部分兼容,C++ 允许您声明与现有类型类名称匹配的 typedef 别名,但前提是该别名引用具有完全相同名称的类型类 。在上面的示例中,第 2 行的 typedef 声明从 C++ 的角度来看是不正确的,但第 3 行的声明是正确的 。
21. 在 C 中,您可以使用与现有类型名称相匹配的字段名称 。
typedef int I;struct S{I I;};在 C++ 中,标识符的这种“重新定义”是不允许的 。
22. 在 C 中,声明相同变量时外部链接和内部链接之间的隐式冲突会导致未定义的行为,但在 C++ 中,这种冲突会使程序格式错误 。要安排这样的冲突,您需要构建一个相当棘手的配置:


推荐阅读