C/C++的区别

1、new 和 malloc 的区别

  malloc与free是C/C++语言的标准库函数,new/delete是C++的运算法。它们都可用于申请动态内存和释放内存。
  对于非内部数据类型的对象而言,只用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
  因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。new/delete不是库函数,而是运算符。

2、函数重载(重定义),C++函数符号的生成规则

函数重载

函数重载指的是在相同作用域内,可以有一组具有相同函数名、不同参数列表的函数。

**函数重载三要素:**同名、不同参数、同作用域(同一个类、同一个命名空间等)。
函数返回值可以相同也可以不同,返回值不能作为重载的依据。

函数重载的必要性
1、避免了为实现同一个功能的函数取很多个名字;
2、类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,要想实例化不同的对象,就很麻烦了;
3、操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用。

C语言为什么不支持函数重载
编译器在编译.c文件时,只会给函数进行简单的重命名:在函数名之前加上“_”,所以两个函数名相同的函数在编译之后的函数名也相同,以至于调用时不知道具体调用哪一个函数。

C++底层是如何处理函数重载的
在.cpp文件中,虽然两个函数名相同,但是它们在符号表中生成的名字不相同。以“?”开头,再加函数名,“@@YA”表示参数列表开始,后面3个字符分别表示返回值类型,参数类型,“@Z”表示结束。由于两个相同函数名生成的符号并不相同,所以编译可以通过。

C++函数符号的生成规则

_cdecl调用约定:“?”+函数名+参数表的开始标识 “@@YA” + 函数返回类型代号+参数类型代号 +结束标识“@Z”或“Z”(无参数)。

_stdcall调用约定:“?”+函数名+参数表的开始标识“@@YG”+函数返回类型代号+参数类型代号 +结束标识“@Z”或“Z”(无参数)。

_fastcal调用约定:“?”+函数名+参数表的开始标识 “@@YI”+ 函数返回类型代号+参数类型代号 +结束标识“@Z”或“Z”(无参数)。

_thiscall调用约定(类成员方法的约定):“?” +函数名+ “@”字符引导的类名+参数表的开始标识(函数性质决定)+ 函数返回类型代号+参数类型代号 +结束标识“@Z”或“Z”(无参数)。

参数表的开始标识
公有(public)成员函数的标识“@@QAE”;
保护(protected)成员函数的标识“@@IAE”;
私有(private)成员函数“@@AAE”;
如果使用Const ,则对应的标识“@@QBE”,“@@IBE”,“@@ABE”。

3、引用与指针的区别

(1)非空区别
  在任何情况下都不能使用指向空值的引用。一个引用必须总是指向某些对象。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这是你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这是你就可以把变量声明为引用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高。
(2)合法性区别
  在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。
(3)可修改区别
  指针与引用的另一个重要的区别是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。
(4)应用区别
  总的来说,在以下情况下应该使用指针:一是考虑到存在不指向任何对象的可能(在这种情况下,能够设置指针为空),二是需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么应该使用引用。

4、inline 函数的特点,与宏,普通函数,static 修饰的函数的区别

inline函数的特点

inline这个名称就可以反映出它的工作方式,函数会在它所调用的位置上展开。这么做可以消除函数调用和返回所带来的开销(寄存器存储和恢复),而且,由于编译器会把调用函数的代码和函数本身放在一起优化,所以也有进一步优化代码的可能。不过这么做是有代价的,代码会变长,这就意味着占用更多的内存空间或者占用更多的指令缓存。内核开发者通常把那些对时间要求比较高,而本身长度又比较短的函数定义成内联函数。如果你把一个大块头的程序做成了内联函数,却不需要争分夺秒,反而反复调用它,这么做就失去了内联的意义了。

总结:对于简短的函数并且调用次数比较多的情况,适合使用内联函数。

优点:
1)inline定义的内联函数,函数代码被放入符号表中,在使用时进行替换(像宏一样展开),效率很高。
2)类的内联函数也是函数。编绎器在调用一个内联函数,首先会检查参数问题,保证调用正确,像对待真正函数一样,消除了隐患及局限性。
3)inline可以作为类的成员函数,刀可以使用所在类的保护成员及私有成员。

缺点:
内联函数以复制为代价,活动产函数开销
1)如果函数的代码较长,使用内联将消耗过多内存
2)如果函数体内有循环,那么执行函数代码时间比调用开销大。

inline函数与普通函数、宏、static修饰的函数的区别

1、inline与普通函数的区别
① 普通函数调用需要开辟栈帧和回收栈帧(清栈),内联函数不开辟和回收栈帧,在调用处展开代码

②普通函数会在编译完生成函数名对应的符号,链接的时候在符号表上可以找到,内联函数不生成符号

③内联函数可以放在头文件中,方便调用,但是普通函数放在头文件中,多个文件编译用到一个头文件,可能会产生函数的重定义

2、inline与static修饰的函数的区别
①二者都只在当前文件可见;
②static修饰变量或者函数,inline只修饰函数

static修饰的函数的作用:
  调用这个函数不会访问或者修改任何对象数据成员

static函数与普通函数的区别:
  用static修饰的函数,本限定在本源码文件中,不能被本源码文件以外的代码文件调用。而普通的函数,默认是extern的,可以被其它代码文件调用该函数。
  在函数的返回类型前加上关键字static,函数就被定义成为静态函数。普通 函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。

因此定义静态函数有以下优点:
① 其他文件中可以定义相同名字的函数,不会发生冲突。
② 静态函数不能被其他文件所用。

3、inline与宏的区别
①宏在预编译阶段进行字符替换,没有安全检查,内联函数在编译阶段展开,编译阶段有安全检查,内联函数相对宏更安全

②宏无法调试,内联函数可以调试

③内联是一种更安全的宏

5、C/C++的相互调用

1、导出C函数以用于C或C++的项目

  如果使用C语言编写的DLL,希望从中导出函数给C或C++的模块访问,则应使用 __cplusplus 预处理器宏确定正在编译的语言。如果是从C++语言模块使用,则用C链接声明这些函数。如果使用此技术并为DLL提供头文件,则这些函数可以原封不动地由C和C++模块使用。

  如果需要将C函数链接到C++可执行文件,并且函数声明头文件没有使用上面的技术,则在C++源文件中添加下列内容以防止编译器修饰C函数名

extern "C" 
{
#include "MyCHeader.h"
}

该代码告诉编译器"MyCHeader.h"是C写的,不要修饰头文件中的C函数名,否则连接的时候会找不到。

2、导出 C++ 函数以用于C语言项目

  如果在用C++编写的DLL中有希望从C语言模块访问的函数,应使用C链接而不是C++链接来声明这些函数。除非另外指定,C++编译器使用C++类型安全命名约定(也称作名称修饰)和C++调用约定(使用此调用约定从C调用会很困难)。

  若要指定 C 链接,请在DLL中为函数声明指定 extern “C”。例如:

extern "C" __declspec( dllexport ) int MyFunc(long parm1);

在C语言的函数中是无法直接调用C++代码的,如果要调用,可以做一个wrapper,例如call_Lib_CPPFunction,它的声明和实现如下:

// wrapper function
extern "C" void call_Lib_CPPFunction(Lib* p, DataAttribute* dataAttribute) 
{
    p->daFun(dataAttribute);
} 

// daFun才是我们C++代码的实现
void Lib::daFun(DataAttribute* dataAttribute)
{
    map<string, MMSINFO>::iterator it;
    // ...
}

3、C++调用C库

  C++调用C库较为简单,由于C++是向前(向C)兼容的,很容进行调用。需要注意的是,在进行C库头文件包含的时候,使用

#ifdf __cplusplus
extern "C" {
#endif

进行包含,指定g++用C的方式生成符号

4、C调用C++库

  C调用C++库,应该是我们常遇到的。linux侧的代码基本是C语言开发。我们有时需要利用一些已开发好的模块,而这些模块可能是C++写好的,我们直接引用,避免重复开发。

一般有以下两种情况:

①只提供给库文件和头文件
  若第三方,只提供给库和头文件,C是不能直接调用的。因为库中的符号表已经按照g++的语法建立。C语言包含头文件之后,按照C语言的语法进行连接,是找不到符号的。我们可以做一个中间接口库,对C++库进行二次封装,C文件直接调用中间接口库即可。

②提供给了源码
  这种情况就较为简单了,将CPP文件中对外的接口,一致要求使用C语言的规则建立符号表即可。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值