您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

C++ 不支持的 C 元素

时间:2023-01-07 11:38:20  来源:今日头条  作者:启辰8

 

C 是用于开发系统软件和任何微处理器软件的经典语言。 linux,大部分windowsmacOS都是写在上面的。 如果你使用任何现代可穿戴小工具或电子设备,在大多数情况下它们也会在 C 程序的控制下运行。 世界上有大量的代码是用 C 语言编写的,而且还会有更多。

C++ 是那些同时需要 C 的所有功能和面向对象编程的灵活性的人的选择。 Counter-Strike、StarCraft 和 World of Warcraft 是用 C++ 编写的,这意味着您可以将 C 的性能与现代技术相结合。 Unity 引擎的一部分也是用 C++ 编写的,以直接访问系统内存和资源。

简单描述一下这些语言的区别,C++是C的改进版。这些语言99%的语法和命令都是一样的,只是C更多的是结构化和过程化编程,而C++是面向对象的。

在本文中,我将分享一个 C 代码示例列表,这些示例不是 C++ 正确的或表现出某些 C 特定行为。 请注意,它是一个方向:C 代码,从 C++ 的角度来看,这是不正确的。

当然,C 语言与 C++ 语言有许多显着差异,任何人都不难举出基于关键字或其他明显的 C99 独有特性的不兼容示例。 您不会在此列表中找到它。 我选择示例的主要标准是,对于 C++ 观察者来说,代码在第一眼看来应该足够“无辜”(即,不包含明显的 C 独占代码),但仍然是特定于 C 语言的。

*对于 [C23],我将标记与 C23 发布无关的项目。

26 元素列表

1.在C中,在用字符串文字初始化字符数组时允许“丢失”尾随的:

char s[4] = "1234";

在 C++ 中,这样的初始化是不正确的。

2. C 支持暂定义。 在一个翻译单元中,您可以在没有初始化器的情况下对同一对象进行多个外部定义:

int a;
int a;
int a, a, a;

C++ 中不允许这样的多重定义。

3. C 语言允许定义不完整类型的外部对象,前提是该类型在同一翻译单元的某个地方被重新定义并变得完整:

struct S s; 
struct S { int i; };

在基本原理层面,这种可能性很可能只是前一段的结果,即支持暂定定义。从 C++ 的角度来看,上述声明顺序是不正确的:C++ 语言立即禁止定义不完整类型的对象 .

4. 在C 中,可以使不完整类型的非定义实体声明为void。

extern void v;

但是在C中不能做相应的定义,因为void是一个不完整的类型。在C++中,你甚至不能做非定义声明。

5. C 语言允许使用 const 限定符定义变量而无需显式初始化:

void foo(void)
{
  const int a;
}

在 C++ 中,这样的定义是不正确的。

6. C 语言允许在 cast 运算符、sizeof 运算符和函数声明(返回类型和参数类型)中声明新类型:

int a = sizeof(enum E { A, B, C }) + (enum X { D, E, F }) 0;
/* 下面的代码使用上面的声明 */
enum E e = B; 
int b = e + F;

C++ 中不允许这样的声明。

7. 在 C 中,函数参数列表中提到的“不熟悉的”结构类型名称是该函数本地新类型的声明。 同时,在函数参数列表中,可以将此类型声明为不完整的,对函数体中已有的完整类型进行“追加声明”:



void foo(struct S *p)   
{
   struct S { int a; }s; 
   p = &s;
   p->a = 5;
}

在这段代码中,从 C 语言的角度来看,一切都是正确的:p 与 &s 具有相同的类型,并且包含字段 a。

从 C++ 语言的角度来看,在函数参数列表中提到一个“陌生”的类类型名称也是一种新类型的声明。 然而,这个新类型不是本地的:它被认为属于封闭的命名空间。 因此,从C++语言的角度来看,函数体中类型S的局部定义与参数列表中提到的类型S无关。 由于类型不匹配,分配 p = &s 是不可能的。 从 C++ 的角度来看,上面的代码是不正确的。

8. C 语言允许将控制转移到“跳过”其初始化声明的自动变量的范围:

switch (1)
{
  int a = 42;
case 1:;
}

从 C++ 的角度来看,这种控制转移是不允许的。

9. 自C99 以来,C 语言中出现了隐式块:一些语句本身就是块,此外,还引入了嵌套的子块。 例如,for 循环本身就是一个块,循环体是嵌套在 for 循环块中的一个单独的块。 因此,下面的代码在 C 中是合法的:

for (int i = 0; i < 10; ++i)
{ 
  int i = 42; 
}

在循环体中声明的变量 i 与在循环头中声明的变量 i 无关。

在 C++ 语言中,在这种情况下,循环头和循环体都形成一个单一的作用域,这就排除了 i 的“嵌套”声明的可能性。

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 true
int 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++ 中,这种冲突会使程序格式错误。 要安排这样的冲突,您需要构建一个相当棘手的配置:

static int a; /* Internal linking */

void foo(void) 
{ 
  int a; /* Hides external `a`, has no linking */

  {
    extern int a; 
    /* Because external `a` is hidden, declare `a` with internal
        linking. Now `a` is declared with both external 
and internal linking - conflict */ 
  } 
}

在 C++ 中,这样的 extern 声明格式错误。 尽管在 C++ 语言标准中有针对这种异常情况的单独示例,但流行的 C++ 编译器通常不会诊断这种违规情况。

以下是我认为微不足道、众所周知且无趣的差异示例。

我将它们包括在这里是为了完整性,因为它们正式满足我的标准:乍一看,代码在 C++ 观察者的眼中看起来或多或少是正常的。

23. C 语言允许从 void * 类型隐式转换指针:

void *p = 0;
int *pp = p;

24. 在 C 中,枚举类型的值可以隐式转换为 int 类型或从 int 类型转换:

enum E { A, B, C } e = A;
e = e + 1;

在 C++ 中,隐式转换只能以一种方式工作。

25. [C23] C语言支持无原型的函数声明:

void foo(); /* Declaration without prototype */

void bar() 
{ 
  foo(1, 2, 3); 
}

26. 在 C 中,嵌套结构类型声明将内部类型的名称放在外部(封闭)范围内:

struct A 
{ 
  struct B { int b; } a;
};

struct B b; /* Refers to the type `struct B` declared on line 3 */

结论

事实上,这就是目前积累的全部。 我希望你觉得我的观察很有趣,它们会对某人有所帮助。你认为我错过了什么重要的事情吗? 请随时留下任何问题、意见或建议。



Tags:C++   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
C 是用于开发系统软件和任何微处理器软件的经典语言。 Linux,大部分Windows和MacOS都是写在上面的。 如果你使用任何现代可穿戴小工具或电子设备,在大多数情况下它们也会在 C...【详细内容】
2023-01-07  Tags: C++  点击:(0)  评论:(0)  加入收藏
导读:使用 C 扩展为 Python 提供特定功能。本文字数:7993,阅读时长大约: 9分钟 使用 C 扩展为 Python 提供特定功能。 在前一篇文章中,我介绍了 opensource.com。在大多数系统上,C...【详细内容】
2023-01-03  Tags: C++  点击:(18)  评论:(0)  加入收藏
CLion是一款专为开发C及C++所设计的跨平台IDE。它是以intellij为基础设计的,包含了许多智能功能来提高开发人员的生产力。这种强大的IDE帮助开发人员在Linux、OS X和Windows...【详细内容】
2022-12-27  Tags: C++  点击:(22)  评论:(0)  加入收藏
我们可以通过 时间度量 - Wall time vs. CPU time 来知道Wall time和CPU time的区别是什么,简单来讲,Wall Time就是类似我们的时钟一样,他没有很精确的表示此时CPU花了多少时...【详细内容】
2022-12-23  Tags: C++  点击:(16)  评论:(0)  加入收藏
数据类型是程序的基础:它告诉我们数据的意义以及我们能在数据上执行的操作。C++语言支持广泛的数据类型,如下: 数据类型选择: 当明确知道数值不可能为负时,选用无符号类型。 使用...【详细内容】
2022-12-08  Tags: C++  点击:(30)  评论:(0)  加入收藏
本文介绍基于C++语言,遍历文件夹中的全部文件,并从中获取指定类型的文件的方法。 首先,我们来明确一下本文所需实现的需求。现在有一个文件夹,其中包含了很多文件,如下图所示;我...【详细内容】
2022-11-16  Tags: C++  点击:(40)  评论:(0)  加入收藏
作者 | 苏宓出品 | CSDN(ID:CSDNnews)如果说此前 Kotlin、Dart、Julia、Carbon 等后起之秀向老牌编程语言发起挑战进攻都是小打小闹,那么这一次 C、C++ 这几种常青藤编程语言则...【详细内容】
2022-11-15  Tags: C++  点击:(40)  评论:(0)  加入收藏
T[N]Built-in array: a fixed-size contiguously allocated sequence of N elements of type T; implicitly converts to a T*内置数组:固定大小的连续分配的T型N个元素序列;...【详细内容】
2022-11-07  Tags: C++  点击:(81)  评论:(0)  加入收藏
前几天有个后端程序员的朋友和小慕吐槽:今年大部分的时间一直在重复着「增删改查」,回顾这一年基本没啥进步,内卷严重啊,这样下去好怕被裁员啊!在现实情况中,好像大多数人都会遭遇...【详细内容】
2022-10-25  Tags: C++  点击:(59)  评论:(0)  加入收藏
经过我前面介绍的C++学习书籍、博客网站、在线视频学习网站(没有看过的同学可以到我的主页翻看),相信大家已经有一些C++基础的,有了理论,需要通过项目来提升自身技术能力,下面推荐...【详细内容】
2022-10-07  Tags: C++  点击:(123)  评论:(0)  加入收藏
▌简易百科推荐
C 是用于开发系统软件和任何微处理器软件的经典语言。 Linux,大部分Windows和MacOS都是写在上面的。 如果你使用任何现代可穿戴小工具或电子设备,在大多数情况下它们也会在 C...【详细内容】
2023-01-07  启辰8  今日头条  Tags:C++   点击:(0)  评论:(0)  加入收藏
一:背景1. 讲故事前些天看到一个奇怪的 Function 函数,调用的是 C# 链接库中的一个 UserLogin 方法,参考代码如下:CREATE FUNCTION dbo.clr_UserLogin( @name AS NVARCHAR(1...【详细内容】
2023-01-03  一线码农聊技术  今日头条  Tags:C#   点击:(15)  评论:(0)  加入收藏
CLion是一款专为开发C及C++所设计的跨平台IDE。它是以intellij为基础设计的,包含了许多智能功能来提高开发人员的生产力。这种强大的IDE帮助开发人员在Linux、OS X和Windows...【详细内容】
2022-12-27    网易号  Tags:C++   点击:(22)  评论:(0)  加入收藏
我们可以通过 时间度量 - Wall time vs. CPU time 来知道Wall time和CPU time的区别是什么,简单来讲,Wall Time就是类似我们的时钟一样,他没有很精确的表示此时CPU花了多少时...【详细内容】
2022-12-23  编编成程  今日头条  Tags:C++   点击:(16)  评论:(0)  加入收藏
数据类型是程序的基础:它告诉我们数据的意义以及我们能在数据上执行的操作。C++语言支持广泛的数据类型,如下: 数据类型选择: 当明确知道数值不可能为负时,选用无符号类型。 使用...【详细内容】
2022-12-08  西子浣纱城  今日头条  Tags:C++   点击:(30)  评论:(0)  加入收藏
本文介绍基于C++语言,遍历文件夹中的全部文件,并从中获取指定类型的文件的方法。 首先,我们来明确一下本文所需实现的需求。现在有一个文件夹,其中包含了很多文件,如下图所示;我...【详细内容】
2022-11-16  疯狂学习GIS  CSDN  Tags:C++   点击:(40)  评论:(0)  加入收藏
在Linux开发环境上学习C语言,对于一个从零开始学习嵌入式的门外汉来说,是一个很好的选择,学习了C语言,又熟悉了Linux 操作系统。1. 开发工具下载及安装:虚拟机 VMware 10下载地址...【详细内容】
2022-11-10  嵌入式er  知乎  Tags:C语言   点击:(67)  评论:(0)  加入收藏
T[N]Built-in array: a fixed-size contiguously allocated sequence of N elements of type T; implicitly converts to a T*内置数组:固定大小的连续分配的T型N个元素序列;...【详细内容】
2022-11-07  小智雅汇  今日头条  Tags:C++   点击:(81)  评论:(0)  加入收藏
前言map 是有序的键值对容器,元素的键是唯一的,值允许重复。用比较函数 Compare 排序键。搜索、移除和插入操作拥有对数复杂度,即O(logn)。底层实现为红黑树。Map定义需要包含...【详细内容】
2022-11-07  君匡  今日头条  Tags:Cpp   点击:(39)  评论:(0)  加入收藏
前几天有个后端程序员的朋友和小慕吐槽:今年大部分的时间一直在重复着「增删改查」,回顾这一年基本没啥进步,内卷严重啊,这样下去好怕被裁员啊!在现实情况中,好像大多数人都会遭遇...【详细内容】
2022-10-25  慕课网  今日头条  Tags:C++   点击:(59)  评论:(0)  加入收藏
站内最新
站内热门
站内头条