当前位置: 首页 > news >正文

C++与C的区别终于说清楚了!

在很大程度上,C++是C的超集,这意味着一个有效的C程序也是一个有效的C++程序。

C和C++的主要区别是,C++支持许多附加特性。但是,C++中有许多规则与C稍有不同。这些不同使得C程序作为C++程序编译时可能以不同的方式运行或根本不能运行。

本节着重讨论这些区别。如果使用C++的编译器编译C程序,就知道这些不同之处。虽然C和C++的区别对本书的示例影响很小,但如果把C代码作为C++程序编译的话,会导致产生错误的消息。

C99标准的发布使得问题更加复杂,因为有些情况下使得C更接近C++。

C99标准的发布使得问题更加复杂,因为有些情况下使得C更接近C++。

例如,C99标准允许在代码中的任意处进行声明,而且可以识别//注释指示符。在其他方面,C99使其与C++的差异变大。

例如,新增了变长数组和关键字restrict。C11缩小了与C++的差异。

例如,引进了char16_t类型,新增了关键字_Alignas,新增了alignas宏与C++的关键字匹配。C11仍处于起步阶段,许多编译器开发商甚至都没有完全支持C99。

我们要了解C90、C99、C11之间的区别,还要了解C++11与这些标准之间的区别,以及每个标准与C标准之间的区别。这部分主要讨论C99、C11和C++之间的区别。当然,C++也正在发展,因此,C和C++的异同也在不断变化。

— 01 —

函数原型

在C++中,函数原型必不可少,但是在C中是可选的。这一区别在声明一个函数时让函数名后面的圆括号为空,就可以看出来。在C中,空圆括号说明这是前置原型,但是在C++中则说明该函数没有参数。也就是说,在C++中,intslice();和int slice(void);相同。例如,下面旧风格的代码在C中可以接受,但是在C++中会产生错误:


int slice();
int main()
{
    ...
    slice(20, 50);
    ...
}
int slice(int a, int b)
{
    ...
}

在C中,编译器假定用户使用旧风格声明函数。在C++中,编译器假定slice()与slice(void)相同,且未声明slice(int,int)函数。

另外,C++允许用户声明多个同名函数,只要它们的参数列表不同即可。

— 02 —

char常量

C把char常量视为int类型,而C++将其视为char类型。例如,考虑下面的语句:

char ch = 'A';

在C中,常量'A'被储存在int大小的内存块中,更精确地说,字符编码被储存为一个int类型的值。相同的数值也储存在变量ch中,但是在ch中该值只占内存的1字节。

在C++中,'A'和ch都占用1字节。它们的区别不会影响本书中的示例。但是,有些C程序利用Char常量被视为int类型这一特性,用字符来表示整数值。例如,如果一个系统中的int是4字节,就可以这样编写C代码:

int x = 'ABCD'; /*对于int是4字节的系统,该语句出现在C程序中没问题

'ABCD'表示一个4字节的int类型值,其中第1个字节储存A的字符编码,第2个字节储存B的字符编码,以此类推。注意,'ABCD'和"ABCD"不同。前者只是书写int类型值的一种方式,而后者是一个字符串,它对应一个5字节内存块的地址。考虑下面的代码:


int x = 'ABCD';
char c = 'ABCD';
printf("%d %d %c %c\n", x, 'ABCD', c, 'ABCD');

在我们的系统中,得到的输出如下:

1094861636 1094861636 D D

该例说明,如果把'ABCD'视为int类型,它是一个4字节的整数值。但是,如果将其视为char类型,程序只使用最后一个字节。在我们的系统中,尝试用%s转换说明打印'ABCD'会导致程序崩溃,因为'ABCD'的数值(1094861636) 已超出该类型可表示的范围。

可以这样使用的原因是C提供了一种方法可单独设置int类型中的每个字节,因为每个字符都对应一个字节。但是,由于要依赖特定的字符编码,所以更好的方法是使用十六进制的整型常量,因为每两位十六进制数对应一个字节。第15章详细介绍过相关内容(C的早期版本不提供十六进制记法,这也许是多字符常量技术首先得到发展的原因)。

— 03 —

const限定符

在C中,全局的const具有外部链接,但是在C++中,具有内部链接。也就是说,下面C++的声明:

const double PI = 3.14159;

相当于下面C中的声明:


static const double PI = 3.14159;

假设这两条声明都在所有函数的外部。C++规则的意图是为了在头文件更加方便地使用const。如果const变量是内部链接,每个包含该头文件的文件都会获得一份const变量的备份。如果const变量是外部链接,就必须在一个文件中进行定义式声明,然后在其他文件中使用关键字extern进行引用式声明。

顺带一提,C++可以使用关键字extern使一个const值具有外部链接。所以两种语言都可以创建内部链接和外部链接的const变量。它们的区别在于默认使用哪种链接。

另外,在C++中,可以用const来声明普通数组的大小:

const int ARSIZE = 100;
double loons[ARSIZE]; /* 在C++中,与double loons[100];相同 */

当然,也可以在C99中使用相同的声明,不过这样的声明会创建一个变长数组。在C++中,可以使用const值来初始化其他const变量,但是在C中不能这样做:

const double RATE = 0.06;            // C++和C都可以
const double STEP = 24.5;            // C++和C都可以
const double LEVEL = RATE * STEP;    // C++可以,C不可以

— 04 —

结构和联合

声明一个有标记的结构或联合后,就可以在C++中使用这个标记作为类型名:


struct duo{    int a;    int b;};struct duo m; /* C和C++都可以 */duo n; /* C不可以,C++可以*/

结果是结构名会与变量名冲突。例如,下面的程序可作为C程序编译,但是作为C++程序编译时会失败。因为C++把printf()语句中的duo解释成结构类型而不是外部变量:


#include
float duo = 100.3;
int main(void)
{
    struct duo { int a; int b;};
    struct duo y = { 2, 4};
    printf ("%f\n", duo); /* 在C中没问题,但是在C++不行 */
    return 0;
}

在C和C++中,都可以在一个结构的内部声明另一个结构:

struct box
{
    struct point {int x; int y; } upperleft;
    struct point lowerright;
};

在C中,随后可以使用任意使用这些结构,但是在C++中使用嵌套结构时要使用一个特殊的符号:

struct box ad;          /* C和 C++都可以 */
struct point dot;       /* C可以,C++不行 */
box::point dot;         /* C不行,C++可以 */

— 05 —

枚举

C++使用枚举比C严格。特别是,只能把enum常量赋给enum变量,然后把变量与其他值作比较。不经过显式强制类型转换,不能把int类型值赋给enum变量,而且也不能递增一个enum变量。下面的代码说明了这些问题:

enum sample {sage, thyme, salt, pepper};
enum sample season;
season = sage;              /* C和C++都可以 */
season = 2;                 /* 在C中会发出警告,在C++中是一个错误 */
season = (enum sample) 3;   /* C和C++都可以*/
season++;                   /* C可以,在C++中是一个错误 */

另外,在C++中,不使用关键字enum也可以声明枚举变量:


enum sample {sage, thyme, salt, pepper};
sample season;    /* C++可以,在C中不可以 */

与结构和联合的情况类似,如果一个变量和enum类型的同名会导致名称冲突。

— 06 —

指向void的指针

C++可以把任意类型的指针赋给指向void的指针,这点与C相同。但是不同的是,只有使用显式强制类型转换才能把指向void的指针赋给其他类型的指针。下面的代码说明了这一点:


int ar[5] = {4, 5, 6,7, 8};
int * pi;
void * pv;
pv = ar;            /* C和C++都可以 */
pi = pv;            /* C可以,C++不可以 */
pi = (int * ) pv;   /* C和C++都可以 */

C++与C的另一个区别是,C++可以把派生类对象的地址赋给基类指针,但是在C中没有这里涉及的特性。

— 07 —

布尔类型

在C++中,布尔类型是bool,而且ture和false都是关键字。在C中,布尔类型是_Bool,但是要包含stdbool.h头文件才可以使用bool、true和false。

— 08 —

可选拼写

在C++中,可以用or来代替||,还有一些其他的可选拼写,它们都是关键字。在C99和C11中,这些可选拼写都被定义为宏,要包含iso646.h才能使用它们。

— 09 —

宽字符支持

在C++中,wchar_t是内置类型,而且wchar_t是关键字。在C99和C11中,wchar_t类型被定义在多个头文件中(stddef.h、stdlib.h、wchar.h、wctype.h)。与此类似,char16_t和char32_t都是C++11的关键字,但是在C11中它们都定义在uchar.h头文件中。

C++通过iostream头文件提供宽字符I/O支持(wchar_t、char16_t和char32_t),而C99通过wchar.h头文件提供一种完全不同的I/O支持包。

— 10 —

复数类型

C++在complex头文件中提供一个复数类来支持复数类型。C有内置的复数类型,并通过complex.h头文件来支持。这两种方法区别很大,不兼容。C更关心数值计算社区提出的需求。

— 11 —

内联函数

C99支持了C++的内联函数特性。但是,C99的实现更加灵活。在C++中,内联函数默认是内部链接。在C++中,如果一个内联函数多次出现在多个文件中,该函数的定义必须相同,而且要使用相同的语言记号。例如,不允许在一个文件的定义中使用int类型形参,而在另一个文件的定义中使用int32_t类型形参。即使用typedef把int32_t定义为int也不能这样做。但是在C中可以这样做。另外,在第15章中介绍过,C允许混合使用内联定义和外部定义,而C++不允许。

— 12 —

C++11中没有的C99/C11特性

虽然在过去C或多或少可以看作是C++的子集,但是C99标准增加了一些C++没有的新特性。下面列出了一些只有C99/C11中才有的特性:

指定初始化器;

受限指针(Restricted pointer) (即,restric指针);

变长数组;

伸缩型数组成员;

带可变数量参数的宏。


 今天的分享就到这里了,大家要好好学C语言/C++哟~
对于准备学习C/C++编程的小伙伴,如果你想更好的提升你的编程核心能力(内功)不妨从现在开始!

C语言C++编程学习交流圈子,企鹅群:

【点击进入】

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!

相关文章:

  • 前端面试知识查漏补缺
  • WEIXIN day_02(8.17) 小程序的组件库
  • 社区交友源码 支持聊天私聊-礼物系统-直播系统-缘分匹配+搭建教程
  • Reactor 之 手把手教你 Spring Boot 整合 Reactor
  • 【42STL-函数对象使用详情】
  • LVS-Nat模式实战
  • java毕业设计基于的测试项目管理平台Mybatis+系统+数据库+调试部署
  • 对于钾,钙,锌,铁,钠,镁金属离子荧光探针的详细知识整理如下
  • Soft Actor-Critic(SAC算法)
  • C语言的头文件的处理
  • 使用 DM binary 部署 DM 集群
  • iOS小技能:RSA签名、验签、加密、解密的原理
  • 使用 Argon2 的 Java 密码散列
  • 基于多次傅里叶变换算法的快速相位解包裹算法研究
  • Mybatis-Plus用纯注解搞定一对多查询
  • C语言笔记(第一章:C语言编程)
  • ECMAScript6(0):ES6简明参考手册
  • laravel5.5 视图共享数据
  • log4j2输出到kafka
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Redis学习笔记 - pipline(流水线、管道)
  • 编写符合Python风格的对象
  • 配置 PM2 实现代码自动发布
  • linux 淘宝开源监控工具tsar
  • 翻译 | The Principles of OOD 面向对象设计原则
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (ros//EnvironmentVariables)ros环境变量
  • (分布式缓存)Redis分片集群
  • (规划)24届春招和25届暑假实习路线准备规划
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (未解决)macOS matplotlib 中文是方框
  • (转)Linux整合apache和tomcat构建Web服务器
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)
  • .NET 依赖注入和配置系统
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法
  • .NET/C# 使用反射注册事件
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .NET中winform传递参数至Url并获得返回值或文件
  • [ 蓝桥杯Web真题 ]-Markdown 文档解析
  • [52PJ] Java面向对象笔记(转自52 1510988116)
  • [C/C++]数据结构 循环队列
  • [C++] sqlite3_get_table 的使用
  • [Editor]Unity Editor类常用方法
  • [ffmpeg] x264 配置参数解析
  • [IE技巧] IE8中HTTP连接数目的变化
  • [NAND Flash 6.1] 怎么看时序图 | 从时序理解嵌入式 NAND Read 源码实现
  • [Oh My C++ Diary]内联函数