C++语言到底是不是C语言的超集之一
wptr33 2025-04-27 16:41 5 浏览
C与C++两个关系亲密的编程语言,它们本质上是两中语言,只是C++语言设计时要求尽可能的兼容C语言特性,因此C语言中99%以上的功能都可以使用C++完成。本文探讨那些存在于C语言中的特性,但是在C++中缺失或者表现出不同行为的特性。了解这些特性能让你更深入地理解这两个语言,但是,本文中所罗列的每一项特性都不是建议你在程序开发中采用的技巧,而恰恰相反是应该避免使用的特性。为了你的代码可读性更好,移植性更强,请不要在你的代码中的任何地方秀出这样的特性。了解它是为了更好地避免她。
C++语言类型检查更强
C语言的类型检查比C++要弱,比如在C语言中void*类型就可以被赋值给char*类型,虽然二者不是同一个类型,甚至二者实际为完全不同的类型,如下图1中的代码,首先定义一个整型变量a,之后使用void指针p1指向整型变量a的地址;再通过char指针p2指向p1,在下图中的第19和20行代码,C编译器是没有提供任何错误的,对于C语言来说,一切都很合理。
但是对于C++编译器来说,void类型的指针是不能够直接赋值给char类型指针的,如上图1中的做法,C++编译器会毫不留情地抛出错误,如下图2所示
对于C++来说,将void*类型的变量赋值给char*的变量,必须显式地指定转换意图,编译器是不会自动为你转换的,他只知道你要把两个不同的东西搞到一起,对他来说是不可接受的。显式地转换类型可以实现你的意图,如图3第20行代码所示,通过使用static_cast类型转换表达式实现转换为目标变量的相同类型。
指针限定符(qualifier)
对于无限定符的指针来说,它不允许或不建议将无限定符的指针赋值给含有限定符的指针,图4中代码,第4行代码为无限定符的指针,第5行代码为含有一个限定符的指针,第6行代码为含有两个限定符的指针。
使用C编译器进行编译图4中的代码时,编译器给出了如下的警告信息,它提示了你进行了两种不兼容类型的指针转换,使用的时候要小心了,出现什么问题自己要负责。
但是,对于C++编译器来说,使用如图6所示的代码。
编译时给出的错误就是如下图7的情景了,图6中第5行代码是合法的,但是第6行代码对于C++编译器来说就是个错误了,它不允许你将int**转换为const int**类型。
枚举类型处理方式
C语言中枚举类型和整型之间的变量进行赋值时,直接使用就是了,整型值可以直接赋值给枚举型变量,同样,枚举值也可以直接赋值给整型变量,如下图8所示的代码。
第6行代码定义一个int类型变量c1,并将其初始化为一个枚举类型的枚举值green,之后通过第9行代码,又将枚举型变量c2赋值给整型变量c3。这两条语句对于C语言来说都是正确的。
但对于C++来说,编译器则不可接受!两种不同类型的变量怎么能够相互赋值呢!如图9所示,它不支持直接将一个整型类型的数值赋值给一个枚举类型的变量。
如果想要在C++中正常地将整型数组赋值给一个枚举变量,那么它需要显示地指定转换目标变量的类型,同样我们可以使用static_cast转换表达式,如图10所示,显示地指定转换后的类型为color枚举值。
常量初始化
C语言中通过const限定符修饰的变量是可以不用指定初始值的,比如下面的代码声明了一个变量i,但是它在类型int前面使用了限定符const,使用C语言编译器编译此代码时,一切正常。
但是如果使用C++的话,使用const限定符的变量必须指定初始化值,否则编译器报如下错误。
虽然不知道C语言这样设计是为什么,也不知道没有初始化的变量以后如何设置它的值,但是目前编译器的行为就是这个样子的,怪怪的,不像是个精心设计的功能,反而给人的感觉倒像是个失误似的。
交叉初始化(crossing initialization)
在C语言中你是可以在goto或switch语句中进行交叉初始化(crossing initialization),但是在C++中你是不能够这样做的。如图13中,采用了goto语句(没事不要使用它,破坏结构化编程逻辑),C语言可以正常编译的。
但是C++是禁止这种用法的,它会给出奇怪的错误,如下图14所示。
全局变量的临时定义
C语言支持在同一个翻译单元(translation unit)中临时性地定义一个全局变量,之后你可以再次定义一个同名的变量,而编译器是不会报错的。这个功能又是一个看上去像个Bug的功能,不知道如何使用,也尽量不要使用这偏门左道技术。
图15中第3行代码和第7行代码都定义了一个整型变量age,C编译器会欣然接受它这种写法,但C++编译器就不客气了,认为它是个语法错误,你重复定了这个age变量,直接抛出下图16中的错误,告诉你不要重复定义之前的变量。
struct、union或enum可以同名
你可以使用enum定义一个枚举类型color,之后再通过typedef定义一个类型为color的结构体,这两种类型是可以同时存在的。见图17中的代码
图17中第2行代码定义了一个color枚举类型color,代码第30-34行又定义了一个color类型的结构体,通过第28行代码定义了一个枚举型变量c1表示颜色枚举值,而第36行代码定义了一个结构体颜色变量,两个color表示不同的类型。但是对于C++来说,就是违反了语法规则,编译器会无情地给出如下图18的错误信息
K&R函数定义
虽然C语言的K&R编程风格不被推荐使用,但是,现在的C编译器依旧保留了C语言的这种写法,比如定义一个函数时,可以采用如图19所示
但是,这种写法对于C++编译器来说是不被允许的,会抛出下图中的错误,它可不认识K&R风格的语法。
函数声明
在C语言中,如果函数原型未指定参数,那么它表示这个函数可以接收一个或多个参数,如果表示这个函数没有参数,则使用关键字void表示。如下图21所示
图21中第3行代码定义了一个名称为f的函数,其参数列表为空,它表示此函数对应的定义可以是接收一个或多个参数,本例中为2个参数,它定义在第12-14行代码之间。第4行代码声明了一个函数,其参数为void,表明这个函数的定义中无需任何参数。
对于C++语言来说,这就是个错误,函数的声明与函数的定义类型不匹配!如图22所示编译器给出的错误。但是C++对于参数列表为空和为void的情况处理方式一致,都表示没有输入参数,其与C语言的行为也是不一样的。
嵌套struct
C语言和C++语言中嵌套struct的作用域范围也是不一样的。在C语言中,嵌套C结构的作用范围与其外层定义的结构相同,但是对于C++来说,它的嵌套结构作用域只存在于这个外层作用域之内。如下图23所示的代码
在C语言中,嵌套结构color为第7-11行代码,在使用color结构时,可以与使用其外层的结构person相同,图23中第15行代码和第23行代码分别定义了一个person和color结构,person为外层结构,color则为嵌套结构,虽然color定义在person结构之内,但这并不影响第23行的用法。但它对于C++来说也是错误,如下图所示
变长数组(Variable-Length-Array)
变长数组是在运行时刻分配数组的大小,而不是在编译时刻决定的。C语言中通过在函数中定义一个数组,这个数组的大小可以是一个参数变量,如图25所示
在图25中,第5行代码定义了一个int型的数组,数组的大小为n,注意这个n是一个变量,数组data就是一个变长数组。第15和16行代码,分别使用不同的参数数值调用f函数,f函数第一次调用中data数组的长度为10,f函数第二次调用中它的长度变为了20。虽然说这是C标准中定义的内容,但是测试使用的gcc和g++都可以编译图25中的代码。而微软visual studio 2022对于图26中C代码或C++代码的代码编译时,都给出了编译错误,如图27所示。
从测试的结果来看,微软提供的C/C++编译器目前是不支持变长数组功能的。
至此,我们介绍了C语言中的一部分特性,但是C++语言没有或者表现不相同的实例代码,并给出了C++编译器提供的错误信息,除此之外,C语言中还有一些其它内容与C++语言表现不一致,下一篇文章我们继续聊C语言的个性,谢谢各位,我们下一篇文章见!
相关推荐
- Linux高性能服务器设计
-
C10K和C10M计算机领域的很多技术都是需求推动的,上世纪90年代,由于互联网的飞速发展,网络服务器无法支撑快速增长的用户规模。1999年,DanKegel提出了著名的C10问题:一台服务器上同时...
- 独立游戏开发者常犯的十大错误
-
...
- 学C了一头雾水该咋办?
-
学C了一头雾水该怎么办?最简单的方法就是你再学一遍呗。俗话说熟能生巧,铁杵也能磨成针。但是一味的为学而学,这个好像没什么卵用。为什么学了还是一头雾水,重点就在这,找出为什么会这个样子?1、概念理解不深...
- C++基础语法梳理:inline 内联函数!虚函数可以是内联函数吗?
-
上节我们分析了C++基础语法的const,static以及this指针,那么这节内容我们来看一下inline内联函数吧!inline内联函数...
- C语言实战小游戏:井字棋(三子棋)大战!文内含有源码
-
井字棋是黑白棋的一种。井字棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、三子旗等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时...
- C++语言到底是不是C语言的超集之一
-
C与C++两个关系亲密的编程语言,它们本质上是两中语言,只是C++语言设计时要求尽可能的兼容C语言特性,因此C语言中99%以上的功能都可以使用C++完成。本文探讨那些存在于C语言中的特性,但是在C++...
- 在C++中,如何避免出现Bug?
-
C++中的主要问题之一是存在大量行为未定义或对程序员来说意外的构造。我们在使用静态分析器检查各种项目时经常会遇到这些问题。但正如我们所知,最佳做法是在编译阶段尽早检测错误。让我们来看看现代C++中的一...
- ESL-通过事件控制FreeSWITCH
-
通过事件提供的最底层控制机制,允许我们有效地利用工具箱,适时选择使用其中的单个工具。FreeSWITCH是一个核心交换与混合矩阵,它周围有几十个模块提供各种功能特性。我们完全控制了所有的即时信息,这些...
- 物理老师教你学C++语言(中篇)
-
一、条件语句与实验判断...
- C语言入门指南
-
当然!以下是关于C语言入门编程的基础介绍和入门建议,希望能帮你顺利起步:C语言入门指南...
- C++选择结构,让程序自动进行决策
-
什么是选择结构?正常的程序都是从上至下顺序执行,这就是顺序结构...
- C++特性使用建议
-
1.引用参数使用引用替代指针且所有不变的引用参数必须加上const。在C语言中,如果函数需要修改变量的值,参数必须为指针,如...
- C++程序员学习Zig指南(中篇)
-
1.复合数据类型结构体与方法的对比C++类:...
- 研一自学C++啃得动吗?
-
研一自学C++啃得动吗?在开始前我有一些资料,是我根据网友给的问题精心整理了一份「C++的资料从专业入门到高级教程」,点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!个人...
- C++关键字介绍
-
下表列出了C++中的常用关键字,这些关键字不能作为变量名或其他标识符名称。1、autoC++11的auto用于表示变量的自动类型推断。即在声明变量的时候,根据变量初始值的类型自动为此变量选择匹配的...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mysql max (33)
- vba instr (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)