C++语言到底是不是C语言的超集之一
wptr33 2025-04-27 16:41 14 浏览
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语言的个性,谢谢各位,我们下一篇文章见!
相关推荐
- 每天一个编程技巧!掌握这7个神技,代码效率飙升200%
-
“同事6点下班,你却为改BUG加班到凌晨?不是你不努力,而是没掌握‘偷懒’的艺术!本文揭秘谷歌工程师私藏的7个编程神技,每天1分钟,让你的代码从‘能用’变‘逆天’。文末附《Python高效代码模板》,...
- Git重置到某个历史节点(Sourcetree工具)
-
前言Sourcetree回滚提交和重置当前分支到此次提交的区别?回滚提交是指将改动的代码提交到本地仓库,但未推送到远端仓库的时候。...
- git工作区、暂存区、本地仓库、远程仓库的区别和联系
-
很多程序员天天写代码,提交代码,拉取代码,对git操作非常熟练,但是对git的原理并不甚了解,借助豆包AI,写个文章总结一下。Git的四个核心区域(工作区、暂存区、本地仓库、远程仓库)是版本控制的核...
- 解锁人生新剧本的密钥:学会让往事退场
-
开篇:敦煌莫高窟的千年启示在莫高窟321窟的《降魔变》壁画前,讲解员指着斑驳色彩说:"画师刻意保留了历代修补痕迹,因为真正的传承不是定格,而是流动。"就像我们的人生剧本,精彩章节永远...
- Reset local repository branch to be just like remote repository HEAD
-
技术背景在使用Git进行版本控制时,有时会遇到本地分支与远程分支不一致的情况。可能是因为误操作、多人协作时远程分支被更新等原因。这时就需要将本地分支重置为与远程分支的...
- Git恢复至之前版本(git恢复到pull之前的版本)
-
让程序回到提交前的样子:两种解决方法:回退(reset)、反做(revert)方法一:gitreset...
- 如何将文件重置或回退到特定版本(怎么让文件回到初始状态)
-
技术背景在使用Git进行版本控制时,经常会遇到需要将文件回退到特定版本的情况。可能是因为当前版本出现了错误,或者想要恢复到之前某个稳定的版本。Git提供了多种方式来实现这一需求。...
- git如何正确回滚代码(git命令回滚代码)
-
方法一,删除远程分支再提交①首先两步保证当前工作区是干净的,并且和远程分支代码一致$gitcocurrentBranch$gitpullorigincurrentBranch$gi...
- [git]撤销的相关命令:reset、revert、checkout
-
基本概念如果不清晰上面的四个概念,请查看廖老师的git教程这里我多说几句:最开始我使用git的时候,我并不明白我为什么写完代码要用git的一些列指令把我的修改存起来。后来用多了,也就明白了为什么。gi...
- 利用shell脚本将Mysql错误日志保存到数据库中
-
说明:利用shell脚本将MYSQL的错误日志提取并保存到数据库中步骤:1)创建数据库,创建表CreatedatabaseMysqlCenter;UseMysqlCenter;CREATET...
- MySQL 9.3 引入增强的JavaScript支持
-
MySQL,这一广泛采用的开源关系型数据库管理系统(RDBMS),发布了其9.x系列的第三个更新版本——9.3版,带来了多项新功能。...
- python 连接 mysql 数据库(python连接MySQL数据库案例)
-
用PyMySQL包来连接Python和MySQL。在使用前需要先通过pip来安装PyMySQL包:在windows系统中打开cmd,输入pipinstallPyMySQL ...
- mysql导入导出命令(mysql 导入命令)
-
mysql导入导出命令mysqldump命令的输入是在bin目录下.1.导出整个数据库 mysqldump-u用户名-p数据库名>导出的文件名 mysqldump-uw...
- MySQL-SQL介绍(mysql sqlyog)
-
介绍结构化查询语言是高级的非过程化编程语言,允许用户在高层数据结构上工作。它不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全不同底层结构的不同数据库系统,可以使用相同...
- MySQL 误删除数据恢复全攻略:基于 Binlog 的实战指南
-
在MySQL的世界里,二进制日志(Binlog)就是我们的"时光机"。它默默记录着数据库的每一个重要变更,就像一位忠实的史官,为我们在数据灾难中提供最后的救命稻草。本文将带您深入掌握如...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
-
- 每天一个编程技巧!掌握这7个神技,代码效率飙升200%
- Git重置到某个历史节点(Sourcetree工具)
- git工作区、暂存区、本地仓库、远程仓库的区别和联系
- 解锁人生新剧本的密钥:学会让往事退场
- Reset local repository branch to be just like remote repository HEAD
- Git恢复至之前版本(git恢复到pull之前的版本)
- 如何将文件重置或回退到特定版本(怎么让文件回到初始状态)
- git如何正确回滚代码(git命令回滚代码)
- [git]撤销的相关命令:reset、revert、checkout
- 利用shell脚本将Mysql错误日志保存到数据库中
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (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)
- c语言 switch (34)
- git commit (34)