您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > 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++   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
C++中的外部模板及其在当前编译文件中的实例化
在C++中,模板是一种泛型编程的工具,它允许程序员以一种类型无关的方式编写代码。然而,模板的一个常见问题是它们会导致编译时间增加,特别是在大型项目中,当多个源文件包含相同的...【详细内容】
2024-04-11  Search: C++  点击:(3)  评论:(0)  加入收藏
C++常见避坑指南
C++ 从入门到放弃?本文主要总结了在C++开发或review过程中常见易出错点做了归纳总结,希望借此能增进大家对C++的了解,减少编程出错,提升工作效率,也可以作为C++开发的避坑攻略。...【详细内容】
2024-04-03  Search: C++  点击:(6)  评论:(0)  加入收藏
C++ 之父反驳白宫警告:自诞生第一天起,C++ 的目标就一直是提高安全性
整理 | 郑丽媛上个月,美国白宫国家网络主任办公室(ONCD)在一份主题为《回到基础构件:通往安全软件之路》的 19 页 PDF 报告中,呼吁开发人员停止使用容易出现内存安全漏洞的编程语...【详细内容】
2024-03-25  Search: C++  点击:(5)  评论:(0)  加入收藏
八个 C++ 开源项目,帮助初学者进阶成长
通过参与或阅读开源项目的源代码,你可以获得丰富的实践机会。实际的项目代码比简单的教程更具挑战性,可以帮助你深入理解 C++ 的各种概念和技术。1.ThreadPool一个简单的 C++1...【详细内容】
2024-03-22  Search: C++  点击:(24)  评论:(0)  加入收藏
C++多线程编程:解锁性能与并发的奥秘
今天我们将深入探讨C++中的多线程编程,揭示多线程如何解锁性能潜力,提高程序的并发性能。什么是多线程?在计算机科学中,多线程是指一个进程(程序的执行实例)中的多个线程同时执行...【详细内容】
2024-02-03  Search: C++  点击:(70)  评论:(0)  加入收藏
C++代码优化攻略
今天我们将深入探讨C++性能优化的世界。在当今软件开发的浪潮中,高性能的代码是必不可少的。无论是开发桌面应用、移动应用,还是嵌入式系统,性能都是关键。1. 选择合适的数据结...【详细内容】
2024-01-26  Search: C++  点击:(117)  评论:(0)  加入收藏
C++质数检测器的设计与实现​
质数,作为数学中的一个基本概念,一直以其独特的性质吸引着众多研究者和爱好者。质数是指大于1的自然数中,除了1和它本身以外不再有其他因数的数。在实际应用中,质数检测也扮演着...【详细内容】
2024-01-15  Search: C++  点击:(117)  评论:(0)  加入收藏
指针变量在C/C++中的内存占用
在编程领域,尤其是C和C++这类底层语言中,指针是一个核心概念,它允许程序直接操作内存地址。然而,关于指针本身在内存中占用的空间大小,却常常让初学者感到困惑。本文将深入探讨这...【详细内容】
2024-01-09  Search: C++  点击:(98)  评论:(0)  加入收藏
C++的面向对象编程:深入解析与理解
当我们谈论C++时,面向对象编程(OOP)是一个无法回避的话题。那么,C++的面向对象究竟是什么?为什么它如此重要?本文将从基本概念到实际应用,为您详细解析C++中的面向对象编程。一、面...【详细内容】
2024-01-03  Search: C++  点击:(97)  评论:(0)  加入收藏
有什么好用的C/C++源代码混淆工具?
开始使用ipaguard前言iOS加固保护是直接针对ios ipa二进制文件的保护技术,可以对iOS APP中的可执行文件进行深度混淆、加密。使用任何工具都无法逆向、破解还原源文件。对APP...【详细内容】
2023-12-29  Search: C++  点击:(119)  评论:(0)  加入收藏
▌简易百科推荐
C++中的外部模板及其在当前编译文件中的实例化
在C++中,模板是一种泛型编程的工具,它允许程序员以一种类型无关的方式编写代码。然而,模板的一个常见问题是它们会导致编译时间增加,特别是在大型项目中,当多个源文件包含相同的...【详细内容】
2024-04-11  鲨鱼编程  微信公众号  Tags:C++   点击:(3)  评论:(0)  加入收藏
C++常见避坑指南
C++ 从入门到放弃?本文主要总结了在C++开发或review过程中常见易出错点做了归纳总结,希望借此能增进大家对C++的了解,减少编程出错,提升工作效率,也可以作为C++开发的避坑攻略。...【详细内容】
2024-04-03  腾讯技术工程    Tags:C++   点击:(6)  评论:(0)  加入收藏
C++ 之父反驳白宫警告:自诞生第一天起,C++ 的目标就一直是提高安全性
整理 | 郑丽媛上个月,美国白宫国家网络主任办公室(ONCD)在一份主题为《回到基础构件:通往安全软件之路》的 19 页 PDF 报告中,呼吁开发人员停止使用容易出现内存安全漏洞的编程语...【详细内容】
2024-03-25    CSDN  Tags:C++   点击:(5)  评论:(0)  加入收藏
八个 C++ 开源项目,帮助初学者进阶成长
通过参与或阅读开源项目的源代码,你可以获得丰富的实践机会。实际的项目代码比简单的教程更具挑战性,可以帮助你深入理解 C++ 的各种概念和技术。1.ThreadPool一个简单的 C++1...【详细内容】
2024-03-22  AI让生活更美好  微信公众号  Tags:C++   点击:(24)  评论:(0)  加入收藏
C# 中15个值得收藏的开源项目推荐
在开源的世界里,C# 编程语言也占有一席之地。这些开源项目涵盖了多个领域,从框架、库到工具,它们为C#开发者提供了丰富的资源和工具,帮助他们更高效地开发、测试和部署应用程序...【详细内容】
2024-03-20  程序员编程日记  微信公众号  Tags:C#   点击:(31)  评论:(0)  加入收藏
C#异步编程:Task.Run vs. async-await,掌握基础与高级用法
概述:C#中的异步编程有两主要方式:Task.Run用于在后台线程执行同步操作,而async-await更适用于清晰表达异步流程。基础用法展示了它们的简单应用,高级用法则演示了它们的结合使...【详细内容】
2024-03-09  架构师老卢  今日头条  Tags:C#   点击:(28)  评论:(0)  加入收藏
C++多线程编程:解锁性能与并发的奥秘
今天我们将深入探讨C++中的多线程编程,揭示多线程如何解锁性能潜力,提高程序的并发性能。什么是多线程?在计算机科学中,多线程是指一个进程(程序的执行实例)中的多个线程同时执行...【详细内容】
2024-02-03     AI让生活更美好  Tags:C++   点击:(70)  评论:(0)  加入收藏
C++代码优化攻略
今天我们将深入探讨C++性能优化的世界。在当今软件开发的浪潮中,高性能的代码是必不可少的。无论是开发桌面应用、移动应用,还是嵌入式系统,性能都是关键。1. 选择合适的数据结...【详细内容】
2024-01-26  AI让生活更美好  微信公众号  Tags:C++   点击:(117)  评论:(0)  加入收藏
C# 线程本地存储为什么线程间值不一样
为什么用 ThreadStatic 标记的字段,只有第一个线程拿到了初始值,其他线程都是默认值,让我能不能帮他解答一下,尼玛,我也不是神仙什么都懂,既然问了,那我试着帮他解答一下,也给后面类...【详细内容】
2024-01-26  一线码农聊技术  微信公众号  Tags:C#   点击:(70)  评论:(0)  加入收藏
C++质数检测器的设计与实现​
质数,作为数学中的一个基本概念,一直以其独特的性质吸引着众多研究者和爱好者。质数是指大于1的自然数中,除了1和它本身以外不再有其他因数的数。在实际应用中,质数检测也扮演着...【详细内容】
2024-01-15  鲨鱼编程  微信公众号  Tags:C++   点击:(117)  评论:(0)  加入收藏
站内最新
站内热门
站内头条