C和C++的简单区别,C是一门面向过程的语言,抽象化的通用程序设计语言,重点在于数据结构和算法的实现,广泛用于底层开发,而C++是一种面向对象的计算机程序设计语言,它支持过程化程序设计,数据抽象,面向对象程序设计,泛型设计等多种程序设计风格。C++可以说是c的继承,它进一步完善并扩充了C,但是切记C++是C的继承,C++不能完全代替C。本文将从以下八个方面浅谈关于C/C++的区别。
目录
二、动态开辟内存new/delete malloc/free
1、内联函数的概念只在c++中存在。在编译阶段进行处理,它是一种以空间换时间的用法,是在调用点直接展开代码。
一、函数重载
1、函数符号的生成规则
在了解函数重载之前,我们首先了解一下C和C++对于函数符号的生成规则。在C中,函数符号的生成之和函数名有关系,在下列的列子中,两个函数的函数符号都是“_Sum”,所以会报重定义的错误,函数符号是不能相同存在的。
在C++中,函数符号的生成与函数原型有关系,也就是与函数的返回值、函数名以及函数的参数列表(参数类型,参数个数,参数顺序等)有关系。它会生成如下图的符号:
Sum为函数名,YA为调用约定,默认为cd_cel的调用约定,第一个H或者N代表的是返回值(N为double类型,H为int类型),后两个HH或者NN是参数列表。
这也正是说明C不支持函数重载,C++支持函数重载这一区别。
2、函数重载的规则以及要素特点等
有以上分析,我们就知道在只在C++中支持函数重载。那么,c++的函数符号生成规则中提到与函数原型有关,但是这三者并不都能为函数重载提供支持。
函数名:当函数名不同时,所生成的函数符号自然不同,但这不能说是重载,而是重新定义了一个新的函数;
int Sum(int a,int b);//?Sum@@YAHHH@Z
int Add(int a,int b);//?Add@@YAHHH@Z
函数返回值:当函数的返回值不同,其他都相同的时候,所生成的函数符号也不同,但是在调用点进行调用的时候,编译器就会出现二义性,一旦出现编译器没法决断的事情,它就会报错。
int Sum(int a,int b);//?Sum@@YAHHH@Z
double Sum(int a,int b);//?Sum@@YANHH@Z
Sum(10,20);//调用点,编译器无法决定去调用哪个函数;
Int rt = Sum(10,20);//调用点,这个语句会做两件事情,一是调用Sum函数,二是将得到的结果返回赋值rt,同样,在调用的时候编译器无法决定去调用哪个函数。
参数列表:当函数的参数列表不同时,所生成的函数符号也不同。它一不会让编译器产生二义性,二可以正常调用函数。
int Sum(int a,int b);//?Sum@@YAHHH@Z
double Sum(double a,double b);//?Sum@@YAHNN@Z
所以说,参数列表为函数重载提供了支持。也就是说在调用点进行重载决议的时候,不依赖返回值,勿产生二义性。这也是函数重载的重要特点。
函数重载的三要素:同作用域,同名,不同参数。
同名:同名函数间才存在重载这种说法,不同名就是两个独立的函数而已。
不同参数列表:参数列表是为函数重载提供支持,通过不同的参数列表来实现函数重载。
同作用域:
bool Compare(int a,int b)
{
return a > b;
}
bool Compare(double a,double b)
{
return a > b;
}
bool Compare(char *pa,char *pb)
{
return strcmp(pa,pb) > 0;;
}
//正确调用
int main()
{
Compare(10,20);
Compare(10.1,20.3);
Compare("Hello","World");
return 0;
}
//在这次调用中,将bool Compare(int,int);放在main函数中,成为了局部作用域,它可以与其他全局作用域的函数共存,在调用时,调用点会采取就近原则选择调用方。因此会发生如下的错误。
int main()
{
bool Compare(int,int);
Compare(10,20);
Compare(10.1,20.3);//警告:将double转换为了int。数据丢失
Compare("Hello","World");//错误。不能讲指针转换为int
return 0;
}
函数重载也称为重定义,在后期会讲到关于函数覆盖也就是重写。到时候会对两者进行进一步的比较。
二、动态开辟内存new/delete malloc/free
在c中,我们进行动态开辟内存时,用的是malloc函数,释放内存用的是free函数。
函数原型:
Void* malloc(size_t size);
Void free(void *ptr);
在C++中,我们进行动态开辟用的是new关键字,释放内存用的是delete关键字;
那么,malloc和new都有哪些区别呢?
1、New和delete是c++提出的一套动态开辟内存的关键字,兼容原c中的malloc和free函数。
2、malloc:是堆上开辟的,并且是不能进行初始化的。Malloc是一个函数,当开辟失败时,指针返回空。当然也不能开辟常量内存块;
3、new:是在自主存储区域开辟的,可以进行初始化。New是一个关键字,当开辟失败时,系统抛出异常;new是可以开辟常量内存块的。New会调用构造函数,malloc不会。New开辟返回指定类型的指针,而malloc返回void*;也就是说new动态开辟时会做两件事,一是开辟内存,二是进行初始化,而malloc只是简单地开辟内存。
4、Free是一个函数;delete是关键字,会调用析构函数,可以被重载;而ferr不会。
5、动态申请内存成功时,malloc函数返回的是void *,需要通过强制类型转换将void *指针转换成我们所需要的类型,new关键字返回的是该对象类型的指针,不需要进行类型转换;
另外我们需要区别Delete和delete[],
例1:对一个内置类型的开辟和释放:
int *p = new int[10];
//delete p;
delete []p;
对于内置类型内存的释放只需要释放内存块即可,所以delete和delete[]都可以进行释放;
例2:对于类对象的开辟和释放
Test* ptest = new Test[10];//Test是一个类
//delete p;//错误
delete[] ptest;//40 44
在进行内存开辟时,实际上开辟了44个字节,其中多出来的四个字节用来存储调用构造函数的个数。对于对象的释放,采用 delete时,它只能进行一次析构;原因是在开辟内存时调用了构造函数,构造函数所做的事情一是开辟内存二是获取资源。在最后释放的时候也应该要调用析构函数,一是释放内存,二是释放资源。采用delete释放时,系统不会知道需要调用多少次析构,所以没办法将所有的开辟的内存都释放;因此采用delete[],释放10次,它会从多出来的4字节中提取到调用构造的次数,然后进行一次释放。
三、const
在C中,const修饰的变量称为常变量,在编译阶段看常变量有没有做左值,剩下的和普通变量相同;在C++中,const修饰的变量叫做常量,一定要进行初始化;编译阶段把用到的常量的地方替换成常量初始化的值。运行阶段做处理。
Const在修饰c++的变量时,必须满足以下的要求:
- 一定要初始化;
- 不允许常量做左值;
- 不允许间接修改,杜绝间接修改常量的风险;
另外关于const修饰全局变量:
在C中,const修饰的全局变量是globa属性;
在C++中,const修饰的全局变量是local属性;但是可以通过extern将local属性改变为global属性;
四、引用
1、引用传入:
表层:引用是别名;
Int a = 10;
Int& b = a;//b是a的别名
底层:c++中和指针的处理方式相同;再用到引用变量的地方,系统会自动进行解引用;
2、特点
①引用必须要初始化
int a = 10;
int &b = a;
②引用初始化的变量一定要可以取地址;
int a = 10;
int &b = 10;//错误,10是数字,不可以进行取地址;
③引用是不可改变的 ;
int a = 10;
int c = 20;
int &b = a;
b = c;//这是一个赋值语句
&b = c;//不能将int转为int*
int &b = c;//重定义b
④引用只能访问引用变量所引用的内存单元;系统自带解引用的过程。
int a = 10;
int c = 20;
int &b = a;
cout << b << endl;//打印10
cout << &b << endl;//打印a的地址
五、inline内联函数
1、内联函数的概念只在c++中存在。在编译阶段进行处理,它是一种以空间换时间的用法,是在调用点直接展开代码。
在test.cpp中:
inline int Sum(int a,int b)
{
return a+b;
}
在main.cpp中,调用点直接展开代码,在这里的调用是错误的,这就要提到inline的特点了;
int main()
{
Sum(20);
return 0;
}
Inline函数,在本文件可见,并且不开栈也不会清栈,也就是说没有栈帧的开辟和清理的开销。
2、inline和static修饰函数的区别:
共同点:两者都是本文件可见;
区别:①inline 无开栈清栈开销,而static具有开栈和清栈的开销;
②inline是在编译阶段调用点代码展开,而static修饰改变函数的属性为local
3、inline和宏的区别
Inline实在编译阶段进行处理,会进行类型检查和安全检查;
而宏的处理是在预编译阶段,不会进行类型以及安全的检查;
也可以说inline是一种更安全的宏;
4、inline内联的注意事项
①内联写在头文件(.h)中;
②内联只在release版本生效;
③内联只是给编译器的一个建议;不能在递归,循环以及switch中使用内联;
④内联是基于实现的关键字,应该加在定义(实现)点,而不加在声明点;
5、inline的优缺点:
优点:在某种程度上提高代码效率;
缺点:以代码膨胀为代价,浪费空间;
6、使用内联的建议:
开栈和清栈的开销大于执行的开销时,建议可以使用内联函数;
开栈和清栈的开销小于执行的开销时,不建议使用内联;
六、函数的默认值
没有实参 形参有默认值
1.自右向左依次赋予
2.不能重复赋予
3.一般赋在声明上
处理:
当传参的时候,没有实参,就会使用形参中所给的默认值;
七、C/C++的相互调用 extern "C"
1、C++调用C,在源文件.cpp中添加extern "C"
2、C调用C++
C++可以修改的时候,在源文件中加extern "C"
C++不可修改的时候,加中间层.cpp
3、不明确编译,也就是C调用C++/C文件时: 利用#ifdef __cplusplus进行处理
八、名字空间作用域
Using namespace std;//using指示符
using std::cout; using声明
对同名的名字空间作用域进行合并;
#include<iostream> cout cin
namespace std;
#include<string> string
namespace std;
std::cout std::cin std::string
在C文件中,全局作用域和局部作用域
在C++文件中,全局作用域: ::全局作用域访问符
局部作用域
名字空间作用域:namespace加上作用域 名字空间名称::---》名字空间作用域访问符
类作用域:类::---》类作用域访问符