1 C语言和C++中那些不同的地方-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C语言和C++中那些不同的地方

单片机与嵌入式 来源:单片机与嵌入式 2023-12-07 14:29 次阅读

C语言虽说经常和C++在一起被大家提起,但可千万不要以为它们是一个东西。现在我们常用的C语言是C89标准,C++是C++99标准的。C89就是在1989年制定的标准,如今最新的是C11和C++11标准。根据不同的标准,它们的功能也会有所不同,但是越新的版本支持的编译器越少,所以本文在讨论的时候使用的C语言标准是C89,C++标准是C++99.我们来介绍C语言和C++中那些不同的地方。

函数默认值

在C++中我们在定义或声明一个函数的时候,有时会在形参中给它赋一个初始值作为不传参数时候的缺省值,例如:

intFUN(inta=10);

代表没有传参调用的时候,自动给a赋一个10的初始值。然而这种操作在c89下是行不通的,在c语言下这么写就会报错。我们都知道,系统在调用任何一个函数的时候都有函数栈帧的开辟,如果函数有参数则需要压入实参。平常在我们人为给定实参的时候,是按照参数列表从右向左依次将参数通过指令传入寄存器,再通过push指令压入。现在我们已经给定了函数参数的默认值,那么在压实参的时候只需要一步push初始值即可。效率更高。

另外需要注意的是,赋初始值必须从参数列表的右边开始赋值,从左边开始赋值将会出错:

int sum1(int a = 10,int b);        //错误
int sum2(int a,int b = 20);            //正确

因为如果sum1的声明是正确的,那么我们调用的时候怎么调用?

sum1( ,20)//?很可惜这样属于语法错误

调用这么写既然不对那就当然不能这样赋初始值了。

相反,来看下sum2的调用:

sum2(20);//合情合理,没有任何问题

实际在写工程的时候,我们都习惯将函数的声明写在头文件中而非本文件

中,然后在不同的文件中写出它们的定义。那么这种情况可以赋初始值吗?

当然可以,不论是定义还是声明处,只要你遵守从右向左赋的规则就可以。

甚至你还可以这样给初始值:

int  fun(int a ,int b = 10);
int  fun(int a = 20,int b);

眼尖的同学看见了下面的那行代码大喊错误,因为先给左边赋值了!

其实这样声明完全没有问题,两句声明是同一个函数(函数多次声明没有问题),第一句已经给b了一个初始值,运行到第二句时已经等价于

int fun(int a = 20,int b = 10);

但是注意,这两句的顺序不能反转,否则就是错误的。

总结:C89标准的C语言不支持函数默认值,C++支持函数默认值,且需要遵循从右向左赋初始值。

inline内联函数

说到内联函数大家应当不陌生,它又是一个C89标准下C语言没有的函数。它的具体做法和宏非常相似,也是在调用处直接将代码展开,只不过宏它是在预编译阶段展开,而内联函数是在 编译阶段进行处理的。同时,宏作为预处理并不进行类型检查,而inline函数是要进行类型检查的,也就可以称作“更安全的宏”。

内联函数和普通函数的区别:内联函数没有栈帧的开辟回退,一般我们直接把内联函数写在头文件中,include之后就可以使用,由于调用时直接代码展开所以我们根本不需要担心什么重定义的问题——它连符号都没有生成当然不会所谓重定义了。普通函数生成符号,内联函数不会生成符号。

关于inline还需要注意的一点是,我们在使用它的时候往往是用来替换函数体非常小(1~5行代码)的函数的。这种情况下函数的堆栈开销相对函数体大小来说就非常大了,这种情况使用内联函数可以大大提高效率。相反如果是一个需要很多代码才能实现的函数,则不适合使用。一是此时函数堆栈调用开销与函数体相比已经是微不足道了,二是大量的代码直接展开的话会给调试带来很大的不便。三是如果代码体达到一个阈值,编译器会将它变成普通函数。

同时,递归函数不能声明为inline函数。说到底inline只是对编译器的建议,最终能否成功也不一定。同时,我们平常生成的都是debug版本,在这个版本下inline是不起作用的。只有生成release版时才会起作用。

总结:C89没有,在调用点直接展开,不生成符号,没有栈帧的开辟回退,仅在Release版本下生效。一般写在头文件中。

函数重载

C语言中产生函数符号的规则是根据名称产生,这也就注定了c语言不存在函数重载的概念。而C++生成函数符号则考虑了函数名、参数个数、参数类型。需要注意的是函数的返回值并不能作为函数重载的依据,也就是说int sum和double sum这两个函数是不能构成重载的!

我们的函数重载也属于多态的一种,这就是所谓的静多态。

静多态:函数重载,函数模板

动多态(运行时的多态):继承中的多态(虚函数)

使用重载的时候需要注意作用域问题,请看如下代码:

#include
usingnamespacestd;
bool compare(int a,int b)
{
return a > b;
}
bool  compare(double a,double b)
{
return a > b;
}
int main()
{
//bool compare(int a,int b);
compare(10,20);
compare(10.5,20.5);
return 0;
}

我在全局作用域定义了两个函数,它们由于参数类型不同可以构成重载。

此时main函数中调用则可以正确的调用到各自的函数。

但是请看main函数中被注释掉的一句代码。如果我将它放出来,则会提出警告:将double类型转换成int类型可能会丢失数据。这就意味着我们编译器针对下面两句调用都调用了参数类型int的compare。由此可见,编译器调用函数时优先在局部作用域搜索,若搜索成功则全部按照该函数的标准调用。若未搜索到才在全局作用域进行搜索。

总结:C语言不存在函数重载,C++根据函数名参数个数参数类型判断重载,属于静多态,必须同一作用域下才叫重载。

const

这一部分非常重要。在我的另一篇博客“C语言的32个关键字”中对C语言中的const也有所讲解。当中提到了这么一个问题:C语言中被const修饰的变量不是常量,叫做常变量或者只读变量,这个常变量是无法当作数组下标的。然而在C++中const修饰的变量可以当作数组下标使用,成为了真正的常量。这就是C++对const的扩展。

C语言中的const:被修饰后不能做左值,可以不初始化,但是之后没有机会再初始化。不可以当数组的下标,可以通过指针修改。简单来说,它和普通变量的区别只是不能做左值而已。其他地方都是一样的。

C++中的const:真正的常量。定义的时候必须初始化,可以用作数组的下标。const在C++中的编译规则是替换(和宏很像),所以它被看作是真正的常量。也可以通过指针修改。需要注意的是,C++的指针有可能退化成C语言的指针。比如以下情况:

int b = 20;
const int a = b;

这时候的a就只是一个普通的C语言的const常变量了,已经无法当数组的下标了。(引用了一个编译阶段不确定的值)

const在生成符号时,是local符号。即在本文件中才可见。如果非要在别的文件中使用它的话,在文件头部声明:extern cosnt int data = 10;这样生成的符号就是global符号。

总结:C中的const叫只读变量,只是无法做左值的变量;C++中的const是真正的常量,但也有可能退化成c语言的常量,默认生成local符号。

引用

说到引用,我们第一反应就是想到了他的兄弟:指针。引用从底层来说和指针就是同一个东西,但是在编译器中它的特性和指针完全不同。

int a = 10;
int &b = a;
int*p=&a;
//b = 20;
//*p = 20;

首先定义一个变量a = 10,然后我们分别定义一个引用b以及一个指针p指向a。我们来转到反汇编看看底层的实现:

图片

可以看到底层实现完全一致,取a的地址放入eax寄存器,再将eax中的值存入引用b/指针p的内存中。至此我们可以说(在底层)引用本质就是一个指针。了解了底层实现,我们回到编译器。我们看到对a的值的修改,指针p的做法是*p = 20;即进行解引用后替换值。底层实现:

图片

再来看看引用修改:

图片

我们看到修改a的值的方法也是一样的,也是解引用。只是我们在调用的时候有所不同:调用p时需要*p解引用,b则直接使用就可以。由此我们推断出:引用在直接使用时是指针解引用。p直接使用则是它自己的地址。这样我们也了解了,我们给引用开辟的这块内存是根本访问不到的。如果直接用就直接解引用了。即使打印&b,输出的也是a的地址。

注:"*"的作用是引用指针指向的变量值,引用其实就是引用该变量的地址,“解”就是把该地址对应的东西解开,解出来,就像打开一个包裹一样,那就是该变量的值了,所以称为“解引用”。也就是说,解引用是返回内存地址中对应的对象。

在此附上将指针转为引用的小技巧:

int*p=&a
/*我们将引用符号移到左边将*替换即可:*/
int&p=a

接下来看看如何创建数组的引用:

int array[10] = {0};//定义一个数组

我们知道,array拿出来使用的话就是数组array的首元素地址。即是int *类型。那么&array是什么意思呢?int **类型,用来指向array[0]地址的一个地址吗?不要想当然了,&array是整个数组类型。

那么要定义一个数组引用,按照上面的小诀窍,先来写写数组指针吧:

int (*q) [10] = &array;

将右侧的&对左边的*进行覆盖:

int (&q)[10] = array;

测试sizeof(q) = 10。我们成功创建了数组引用。经过上面的详解 ,我们知道了引用其实就是取地址。那么我们都知道一个立即数是没有地址的,即

int &b = 10;

这样的代码是无法通过编译的。那如果你就是非要引用一个立即数,其实也不是没有办法:

const int &b  = 10;

即将这个立即数用const修饰一下,就可以了。为什么呢?

这时因为被const修饰的都会产生一个临时量来保存这个数据,自然就有地址可取了。

总结:引用底层就是指针,使用时会直接解引用,可以配合const对一个立即数进行引用。

malloc,free && new,delete

这个问题很有意思,也是重点需要关注的问题。malloc()和free()是C语言中动态申请内存和释放内存的标准库中的函数。而new和delete是C++运算符、关键字。new和delete底层其实还是调用了malloc和free。它们之间的区别有以下几个方面:

①:malloc和free是函数,new和delete是运算符。

②:malloc在分配内存前需要大小,new不需要。

例如:

int *p1 = (int *)malloc(sizeof(int));
int *p2 = new int;     //int *p3 = new int(10);

malloc时需要指定大小,还需要类型转换。new时不需要指定大小因为它可以从给出的类型判断,并且还可以同时赋初始值。

③:malloc不安全,需要手动类型转换,new不需要类型转换。

详见上一条。

④:free只释放空间,delete先调用析构函数再释放空间(如果需要)。

与第⑤条对应,如果使用了复杂类型,先析构再call operator delete回收内存。

⑤:new是先调用构造函数再申请空间(如果需要)。

与第④条对应,我们在调用new的时候(例如int *p2 = new int;这句代码 ),底层代码的实现是:首先push 4字节(int类型的大小),随后call operator new函数分配了内存。由于我们这句代码并未涉及到复杂类型,如类类型,所以也就没有构造函数的调用。如下是operator new的源代码,也是new实现的重要函数:

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{       // try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{       // report no memory
_THROW_NCEE(_XSTD bad_alloc, );
}
return (p);
}

我们可以看到,首先malloc(size)申请参数字节大小的内存。

如果失败(malloc失败返回0)则进入判断:

如果_callnewh(size)也失败的话,抛出bad_alloc异常。

_callnewh()这个函数是在查看new handler是否可用。

如果可用会释放一部分内存再返回到malloc处继续申请。

如果new handler不可用就会抛出异常。

⑥:内存不足(开辟失败)时处理方式不同。

malloc失败返回0,new失败抛出bad_alloc异常。

⑦:new和malloc开辟内存的位置不同。

malloc开辟在堆区,new开辟在自由存储区域。

⑧:new可以调用malloc(),但malloc不能调用new。

new就是用malloc()实现的,new是C++独有malloc当然无法调用。

作用域

C语言中作用域只有两个:局部,全局。C++中则是有:局部作用域,类作用域,名字空间作用域三种。

所谓名字空间就是namespace,我们定义一个名字空间就是定义一个新作用域。访问时需要以如下方式访问,以std为例:

std::cin<< "123" <

例如我们有一个名字空间叫Myname,其中有一个变量叫做data。如果我们希望在其他地方使用data的话,需要在文件头声明:using Myname::data;这样一来data就使用的是Myname中的值了。可是这样每个符号我们都得声明岂不是累死?我们只要using namespace Myname;就可以将其中所有符号导入了。所以我们经常看到如下代码:

using namespace std;

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    180

    文章

    7604

    浏览量

    136673
  • 函数
    +关注

    关注

    3

    文章

    4327

    浏览量

    62567
  • C++
    C++
    +关注

    关注

    22

    文章

    2108

    浏览量

    73616
  • 编译器
    +关注

    关注

    1

    文章

    1623

    浏览量

    49107
收藏 人收藏

    评论

    相关推荐

    C语言和C++哪一个更好用

    在嵌入式软件程序开发C语言无疑是最常被使用的程序语言。不过应该明白的是,有些嵌入式硬件同时提供C语言
    发表于 11-08 07:46

    C语言和C++编程的一些思考资料说明

    最原始的方式对函数进行命名。学过编译原理的同学就会知道,最原始的C++编译器其实就是将C++转化成C语言,然后用C
    发表于 05-09 18:16 1次下载
    <b class='flag-5'>C</b><b class='flag-5'>语言和</b><b class='flag-5'>C++</b>编程的一些思考资料说明

    MATLAB 64位C语言和C++编译器应用程序免费下载

    本文档的主要内容详细介绍的是MATLAB 64位C语言和C++编译器应用程序免费下载。
    发表于 05-21 08:00 4次下载
    MATLAB 64位<b class='flag-5'>C</b><b class='flag-5'>语言和</b><b class='flag-5'>C++</b>编译器应用程序免费下载

    C++语言和面向对象程序设计教程

    C++语言和面向对象程序设计代表了旨在使计算机问题解更加符合人的思维活动,是软件开发方法的一场革命;面向对象建模和面向对象设计与实现在软件开发生命周期中起着关键作用。 全书共有14章和一个附录。第1
    发表于 03-02 08:00 6次下载

    使用C语言和C++编写俄罗斯方块的资料和源代码免费下载

    本文档的主要内容详细介绍的是使用C语言和C++编写俄罗斯方块的资料和源代码免费下载。
    发表于 06-10 08:00 4次下载
    使用<b class='flag-5'>C</b><b class='flag-5'>语言和</b><b class='flag-5'>C++</b>编写俄罗斯方块的资料和源代码免费下载

    详谈C语言和C++的区别和联系

    在学习了C语言和C++之后,这两者之间的区别我们需要仔细的捋一捋!
    的头像 发表于 06-29 14:56 5756次阅读
    详谈<b class='flag-5'>C</b><b class='flag-5'>语言和</b><b class='flag-5'>C++</b>的区别和联系

    CC++是一回事吗

    C89,C++标准是C++99。 我们来介绍C语言和C++
    的头像 发表于 11-13 18:18 3282次阅读

    C语言和C++的特点与用法详细说明

    本文档的主要内容详细介绍的是C语言和C++的特点与用法详细说明。
    的头像 发表于 12-26 10:58 4402次阅读

    嵌入式程序开发,C语言和C++究竟应该用哪个?

    在嵌入式软件程序开发C语言无疑是最常被使用的程序语言。不过应该明白的是,有些嵌入式硬件同时提供C语言
    发表于 11-03 14:21 60次下载
    嵌入式程序开发,<b class='flag-5'>C</b><b class='flag-5'>语言和</b><b class='flag-5'>C++</b>究竟应该用哪个?

    C语言C++面试知识点总结

    相对而言,C语言和C++相关的面试题比较少见,没有Java方向写的人那么多,这是一篇 C 语言C++
    的头像 发表于 05-12 14:59 1451次阅读

    C语言C++面试知识点总结

    相对而言,C语言和C++相关的面试题比较少见,没有Java方向写的人那么多,这是一篇 C 语言C++
    的头像 发表于 05-13 11:59 1856次阅读

    C语言和C++到底是什么关系

    首先C++C语言本来就是两种不同的编程语言,但C++确实是对C
    的头像 发表于 06-20 11:28 4945次阅读

    如何选择创建c语言和c++

    选择创建 C 语言和 C++ 都需要综合考虑多个因素。在决定使用哪种语言之前,我们需要对这两种语言的特点、优缺点、适用场景、学习成本等进行全
    的头像 发表于 11-27 15:58 598次阅读

    vb语言和c++语言的区别

    VB语言和C++语言是两种不同的编程语言,虽然它们都属于高级编程语言,但在设计和用途上有很多区别。下面将详细比较VB
    的头像 发表于 02-01 10:20 2247次阅读

    C语言和C++结构体的区别

    同样是结构体,看看在C语言和C++中有什么区别?
    的头像 发表于 10-30 15:11 196次阅读