c++起始(名词修饰,extern “C” ,引用)
名字修饰(name Mangling)
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
Name Mangling是一种在编译过程中,将函数、变量的名称重新改编的机制,简单来说就是编译器为了区分各 个函数,将函数通过某种算法,重新修饰为一个全局唯一的名称。
C语言的名字修饰规则非常简单,只是在函数名字前面添加了下划线。比如,对于以下代码,在后链接时就 会出错:
int Add(int left, int right);int main()
{ Add(1, 2);
return ;
}
编译器报错:error LNK2019: 无法解析的外部符号 _Add,该符号在函数 _main 中被引用。
上述Add函数只给了声明没有给定义,因此在链接时就会报错,提示:在main函数中引用的Add函数找不到函 数体。从报错结果中可以看到,C语言只是简单的在函数名前添加下划线。因此当工程中存在相同函数名的函 数时,就会产生冲突。
具体例子看上篇博客有详细介绍。
由于C++要支持函数重载,命名空间等,使得其修饰规则比较复杂,不同编译器在底层的实现方式可能都有差 异。
int Add(int left, int right);int main() { Add(1, 2); return 0; }
int Add(int left, int right); double Add(double left, double right);int main() { Add(1, 2); Add(1.0, 2.0); return 0; }
在vs下,对上述代码进行编译链接,后编译器报错:
error LNK2019: 无法解析的外部符号 “double cdecl Add(double,double)” (?Add@@YANNN@Z) error LNK2019: 无法解析的外部符号 “int __cdecl Add(int,int)” (?Add@@YAHHH@Z)
通过上述错误可以看出,编译器实际在底层使用的不是Add名字,而是被重新修饰过的一个比较复杂的名字, 被重新修饰后的名字中包含了:函数的名字以及参数类型。这就是为什么函数重载中几个同名函数要求其参数 列表不同的原因。只要参数列表不同,编译器在编译时通过对函数名字进行重新修饰,将参数类型包含在终 的名字中,就可保证名字在底层的全局唯一性。
extern “C”
c++的函数库,c语言中并不能直接用。
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将 该函数按照C语言规则来编译
extern "C" int Add(int left, int right);int main(){ Add(1,2); return 0;}
链接时报错:error LNK2019: 无法解析的外部符号_Add,该符号在函数 _main 中被引用
而不是c++中的报错。
引用
在之前的学习中,我们传参数的两种方式分别为,传值和传地址。
有没有什么方法能够将让传值的方式也能够起到交换的作用?
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引 用的变量共用同一块内存空间。
使用方法
类型& 引用变量名(对象名) = 引用实体;
注意:引用类型必须和引用实体是同种类型的
引用特性
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体,再不能引用其他实体
常引用
因为10本身是常量,修改不了
类型不同
使用场景
做参数
void Swap(int& left, int& right)
{ int temp = left;
left = right;
right = temp
}做返回值
int& TestRefReturn(int& a)
{ a += 10;
return a;
}
int& Add(int a, int b)
{ int c = a + b; return c; }int main() { int& ret = Add(1, 2); Add(3, 4); cout << "Add(1, 2) is :"<< ret <<endl; return 0; }
注意:如果函数返回时,离开函数作用域后,其栈上空间已经还给系统,因此不能用栈上的空间作为引用类型 返回。如果以引用类型返回,返回值的生命周期必须不受函数的限制(即比函数生命周期长)。
具体参考https://blog.csdn.net/qq_40550018/article/details/81225519函数栈帧
传值、传引用效率比较
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实 参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回 值类型非常大时,效率就更低
#include <time.h> struct A { int a[10000]; };void TestFunc1(A a) {}void TestFunc2(A& a) {}void TestRefAndValue() { A a;// 以值作为函数参数 size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc1(a); size_t end1 = clock();// 以引用作为函数参数 size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc2(a); size_t end2 = clock();// 分别计算两个函数运行结束后的时间cout << "TestFunc1(int*)-time:" << end1 - begin1 << endl; cout << "TestFunc2(int&)-time:" << end2 - begin2 << endl;}// 运行多次,检测值和引用在传参方面的效率区别
int main(){ for (int i = 0; i < 10; ++i) { TestRefAndValue(); } return 0; }
值和引用的作为返回值类型的性能比较
#include <time.h> struct A { int a[10000]; };A a;A TestFunc1() { return a; }A& TestFunc2() { return a; }void TestReturnByRefOrValue() { // 以值作为函数的返回值类型 size_t begin1 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc1(); size_t end1 = clock();// 以引用作为函数的返回值类型 size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc2(); size_t end2 = clock();// 计算两个函数运算完成之后的时间 cout << "TestFunc1 time:" << end1 - begin1 << endl; cout << "TestFunc2 time:" << end2 - begin2 << endl; }
通过上述代码的比较,发现传值和指针在作为传参以及返回值类型上效率相差很大。
引用和指针的区别
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
int main() {int a = 10; int& ra = a;cout<<"&a = "<<&a<<endl; cout<<"&ra = "<<&ra<<endl;return 0; }
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的
int main() {
int a = 10;int& ra = a;ra = 20;int* pa = &a;*pa = 20;return 0;}
我们来看下引用和指针的汇编代码对比:
引用和指针的不同点:
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实 体
- 没有NULL引用,但有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4 个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理 8. 引用比指针使用起来相对更安全
c++起始(名词修饰,extern “C” ,引用)相关推荐
- C++ const关键字的总结(全局/局部变量、修饰指针和引用、成员函数和数据成员、修饰类对象、const与宏定义的区别、Static与Const的区别)
const关键字 const关键字 1.什么是const 2.使用原理 2.1.const全局/局部变量 2.2.cosnt修饰指针和引用 2.3.const修饰函数参数 2.4.const修饰函数返 ...
- const修饰指针和引用的用法【转贴】
*************************************************** 更多精彩,欢迎进入:http://shop115376623.taobao.com ****** ...
- 吃人的那些 Java 名词:对象、引用、堆、栈
作为一个有着 8 年 Java 编程经验的 IT 老兵,说起来很惭愧,我被 Java 当中的四五个名词一直困扰着:对象.引用.堆.栈.堆栈(栈可同堆栈,因此是四个名词,也是五个名词).每次我看到这几个 ...
- 【英语】语法:名词修饰名词
现代英语的特色之一,是力求以简单的结构,表达复杂的思维.以名词作修饰语,简称"名词定语"(attributive nouns),便是朝向这目标的手段之一. 所谓名词修饰名词,就是以 ...
- final修饰的是引用还是引用的对象
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的.
- java中final关键字,修饰变量、引用、类、方法、实例变量的问题
一.final在英语中的意思 final在英语中表示 最终的.不可变更的. 二.final修饰的变量 局部变量: final修饰的局部变量一旦赋值就不能再次赋值,只能赋值一次.若是再次赋值会报错: 实 ...
- C语言中extern修饰符的用法
在C语言中,修饰符extern用在变量或者函数的声明前,用来说明"此变量/函数是在别处定义的,要在此处引用". 0. extern修饰变量的声明.举例来说,如果文件a.c需要引用b ...
- C语言中的extern关键字用法
在C语言中,修饰符extern用在变量或者函数的声明前,用来说明"此变量/函数是在别处定义的,要在此处引用". 1. extern修饰变量的声明.举例来说,如果文件a.c需要引用b ...
- C语言中extern的用法--转
http://blog.sina.com.cn/s/blog_52deb9d50100ml6y.html 在C语言中,修饰符extern用在变量或者函数的声明前,用来说明"此变量/函数是在别 ...
最新文章
- VMware Workstation 15 Pro 永久激活密钥 下载
- Entity Framework Code First添加修改及删除单独实体
- Redis 持久化之RDB和AOF
- 用C# (.NET Core) 实现抽象工厂设计模式
- asp.net链接mysql数据库------------【个人收集】
- DolphinPHP
- Eclipse汉化教程
- 五分钟搞定单片机keil5 运行破解安装教程
- H3C nqa 配置
- Fragstats4.2之计算景观格局指数(一)
- VMware虚拟机克隆
- Netty学习笔记(一)--- 初识Netty
- 腾讯海外计费系统架构演进
- 了解ES6 The Dope Way第五部分:类,转译ES6代码和更多资源!
- 关于Hadoop在java客户端进行下载时的异常java.io.FileNotFoundException: Hadoop home directory is no existed的解决办法
- 苹果推iOS游戏手柄,掌上游戏主机格局变天?
- Django作业管理系统(1)
- c语言鸡兔同笼的实训报告的目的,车床实训报告实习目的
- [EULAR文摘] 脊柱放射学持续进展是否显著影响关节功能
- 顾小清:人工智能何以促进未来教育发展
热门文章
- HTML5 之 简单汇总
- 实验三 类的继承和多态性
- 修改窗口图标 AfxRegisterWndClass()
- python中print的用法_python中print用法
- promise将ajax变为同步,ES6-Promise改造异步请求为同步
- mysql count 不等于_Mysql 不同的 count 区别
- 关闭运动轨迹_网球初学者如何正确入门网球运动,有哪些学习细节
- matlabapp窗口图像_matlab – 如何自定义App Designer图形的背景?
- java怎么把文件写入到usb里_如何创建PowerShell脚本以将文件复制到USB闪存驱动器?...
- 南岸焊接机器人厂_造船三部高效焊接工艺技术年鉴