C++基础部分_librobotdefine,2024年最新Golang黑科技保活实现原理揭秘

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

return 0;

}


##### 4.1全缺省



///全缺省
void Func1(int a=10,int b=20,int c=30)
{
cout<<“a=”<<a<<endl;
cout<<“b=”<<b<<endl;
cout<<“c=”<<c<<endl;
}
int main()
{
Func1();
Func1(1);
Func1(1,2);
Func1(1,2,3);
}


##### 4.2半缺省



//半缺省(缺省部分参数),必须从右往左连续缺省
void Func2(int a,int b=10,int c=20)
{
Func2();//error
//调用时如果要传参必须从左往右依次传参。
Func2(1);
Func2(1,2);
Func2(1,2,3);
}



void StackInit(struct Stack* ps,int capacity=4){
ps->a=(int*)malloc(sizeof(int)*capacity);
//
ps->top=0;
ps->capacity=capacity;
}


##### 4.3缺省的注意事项


1. 半缺省参数必须从右往左依次来给出,不能间隔着给
2. 缺省参数不能在函数声明和定义中同时出现


	1. ```
	//a.h
	void TestFunc(int a = 10);
	// a.c
	void TestFunc(int a = 20)
	{}
	// 注意:如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值
	// 实际上在VS编译器中直接报错了。
	
	```
3. 缺省值必须是常量或者全局变量


### **5.函数重载**


#### 5.1函数重载概念


##### 5.1.1重载的判定


一个函数有多种意义或者多个调用方式


* 函数名相同
* 参数不同(满足一个)
	+ 类型
	+ 个数
	+ 类型顺序
		- void func(int i,int j) -->\_Z4funcii
		- void func(int j,int i) -->\_Z4funcii
		- 两者不是重载
* 对返回值没有要求,返回值和重载没有关系



int Add(int l,int r)
{
return l+r;
}
double Add(double l,double r)
{
return l+r;
}
long Add(long l,long r)
{
return l+r;
}
void func1(int i,char ch)
{

}
void func1(char ch,int i)
{

}
void func1()
{

}
int func1()//和上一个不构成重载
{

}
int main()
{
Add(10,20);
Add(10.0,20.0);
Add(10L,20L);
return 0;
}


#### 5.2重载的原理(名字修饰)


1. 什么是函数重载
2. c++是如何支持函数重载的?为什么C语言不支持


得从编译链接说起


list.h list.c test.c


1. 预处理(预编译):头文件的展开/宏替换/条件编译/去掉注释
	1. list.i test.i
2. 编译 :检查语法,生成汇编代码
	1. list.s test.s
3. 汇编 :汇编代码转换成二进制的机器码
	1. list.o test.o
4. 链接 :将两个目标文件链接到一起生成可执行程序(**如果当前文件中有函数的定义,那么编译时就填上地址了;如果在在当前文件中只有函数的声明,只能链接的时候去其他的xxx.o符号表中根据函数修饰名字去找,这就是链接的重要工作**)



test.c

void list_push_back(int x);
void add(int i,int j);
int main()
{
list_push_back(1);
return 0;
}



list.c -->list.o
void list_push_back(int x);
void add(int i,int j);
void list_push_back(int x)
{
printf(“%d\n”,x);
}
void add(int i,int j)
{

}



test.o

call list_push_back(?)
//链接时,这里的问号表示编译时,这个函数我们只有声明,没有定义所以无法找到他的地址
//表示链接的时候,到其他的目标文件的符号表中去找这个函数的地址

符号表:
main:0x31141321



list.o
list_push_back()
{

}

符号表:
list_push_back:0x31144332
add:0x3114432


* C语言是直接用函数名称。两个都是一样的函数名字,无法区分。


因为C++是名字修饰。所以可以。



0000000000000400623<_Z3addii>

000000000000040062f<_Z3adddd>

test.o
// add(1,2);
call _Z3addii(?)
// add(1.1,2.2);
call _Z3adddd(?)


* C++的函数修饰规则(不同编译器不同规则),但都把参数类型加进去了


**\_Z+函数长度+函数名+类型首字母**


![image-20211125210846252](https://img-blog.csdnimg.cn/img_convert/8ae6b8531cdba504e064a250ab42f303.png)


![image-20211125211121715](https://img-blog.csdnimg.cn/img_convert/fc2d923591548907f6c3f78d503a680f.png)


因此函数命名不同了之后链接的时候去其他目标文件中找符号表就能找到对应的函数。



//add(1,2)
call _Z3addii(?) //link的时候去找?的地址
//其他目标文件中_Z3addii符号的地址是000000004007dd
//add(1.1,2.2)
call _Z3adddd(?) //link的时候去找?的地址
//其他目标文件中_Z3adddd符号的地址是000000004007ff


![image-20211125212731369](https://img-blog.csdnimg.cn/img_convert/18bb2d70b9a3715e16643f4c53a49c6b.png)


![image-20211125215458388](https://img-blog.csdnimg.cn/img_convert/23f9c360257a2157c5b1732b1d11f8f6.png)



> 
> C语言不支持函数重载,因为编译的时候,两个重载函数,函数名相同,在同一个目标文件.o中,符号表中存在歧义和冲突。同样的函数名两个函数地址。其次链接的时候也存在歧义和冲突,因为他们都是直接使用函数名去标识和查找。而重载函数,函数名相同。
> 
> 
> 而C++的目标文件符号表中不是直接用函数名来标识和查找函数
> 
> 
> 1. 函数名修饰规则(不同编译器下函数修饰名不同)
> 	1. **\_Z+函数长度+函数名+类型首字母**
> 		1. ![image-20211125220352038](https://img-blog.csdnimg.cn/img_convert/36d61df88ce06b25e3e28d0739df01db.png)
> 2. 有了函数名修饰规则,只要参数不同,目标文件的符号表里面就不存在二义性和冲突了
> 3. 链接的时候,test.o的main函数里面去调用两个重载的函数也是明确的。
> 
> 
> 


#### 5.3extern “C”


vs下实现静态库:


1. 包含对应目录下的头文件
2. 在工程属性中配置静态库目录(链接器的常规中),添加静态库(链接器的输入中)


Cpp调C:在Cpp的#include"…/xxx/xx.h"上下加上extern “C”{}



extern “C”
{
#include"…/xx/Stack.h"
}


C调Cpp:在Cpp的.h文件中加extern “C”{ 函数},Cpp静态库就会按照C的规则去处理以下函数。当然重载就要写两个函数了。


特别注意,因为.c包含了.h的头文件,所以头文件包含会将.h部分在c展开,而cpp才认识extern “C”,C部分不认识extern “C”;


* 第一种做法



#ifdef __cplusplus
extern “C” {
#endif
cpp函数声明;
#ifdef __cplusplus
}
#endif


引入条件编译,在cpp库中,是识别extern "C"并且按照C的方式进行函数推导。当C对其头文件展开的时候,由于不是cpp,条件编译直接声明成C函数。


* 第二种做法



#ifdef __cplusplus
#define EXTERN_C extern “C”
#else
#define EXTERN_C
#endif
EXTERN_C cpp函数声明;
EXTERN_C cpp函数声明;
EXTERN_C cpp函数声明;


\*\*也就是说extern “C” 总是在cpp中的,因为只有cpp 认识 extern " C" \*\*


C++程序调用C的库,在C++程序中加extern “C”


C程序调用C++的库,在C++库中加extern “C”



> 
> 参考文章:https://zhuanlan.zhihu.com/p/361485807
> 
> 
> 


##### 5.3.1C++程序中调用C库


首先为什么C++程序中不能调用C库,会产生链接错误。因为两者对函数名字的命名规则不同,因此C++的链接器会去C模块中查找对应函数,但是找不到。


那我们怎么在C++项目中使用C库模块的?



//util.h
extern “C”
{
int add(int ,int );
}


通过extern “C”,告诉g++编译器,不要对这些函数进行Name mangling,按照C编译器的方式去生成符号表符号。这样在main.c的目标文件(.o)中,参数列表为两个int类型的add函数名称为\_add。链接器可以正确找到util.o中的add函数(他们都是\_add)。


不过注意参数列表为两个double类型的add函数名称还是\_\_Z3adddd。


使用 extern ”C“ 的常见情况是使用第三方提供的编译好的静态链接库(.a/.lib),动态链接库(.so/.dll)。通常我们会拿到一个头文件和对应的编译好的库文件。


在头文件中通过条件编译引入 extern “C”。



//until.h
#ifdef __cplusplus
extern “C” {
#endif

int add(int, int);

#ifdef __cplusplus
}
#endif



gcc -c xxx.c



ar -rc libxxxx.a xxx.o xxx.o



test-static:test.cc
g++ -o $@ $^ -I ./mylib/include -L ./mylib/lib -l util -static
.PHONY:clean
clean:
rm -rf *.o test-static


![image-20220114183816152](https://img-blog.csdnimg.cn/img_convert/934d50283b69ab1594cc4269133cb2b5.png)


![image-20220114193347352](https://img-blog.csdnimg.cn/img_convert/deddb494dd167ec69588c7275b7ac4a1.png)


##### 5.3.2C程序中调用C++函数


假设我们有一个C++类 **Robot**,在文件 **robot.h** 和 **robot.cpp** 中定义。**Robot** 类中有个成员函数 **sayHi()** 我们想在C程序中调用这个函数。


**robot.h**



#pragma once

#include

class Robot
{
public:
Robot(std::string name) : name_(name) {}

void sayHi();

private:
std::string name_;
};


**robot.cpp**



#include

#include “robot.h”

void Robot::sayHi()
{
std::cout << "Hi, I am " << name_ << “!\n”;
}


我们用编译C++代码的方式,使用 **g++** 编译器对这个类进行编译,此时类 **Robot** 并不知道自己会被C程序调用。



g++ -fpic -shared robot.cpp -o librobot.so


接下来用C++创建一个C的接口,定义在 **robot\_c\_api.h** 和 **robot\_c\_api.cpp** 中,这个接口会定义一个C函数 **Robot\_sayHi(const char \*name),** 这个函数会创建一个类 **Robot** 的实例,并调用 **Robot** 的成员函数 **sayHi()。**


**robot\_c\_api.h**



#pragma once

#ifdef __cplusplus
extern “C” {
#endif

void Robot_sayHi(const char *name);

#ifdef __cplusplus
}
#endif


**robot\_c\_api.cpp**



#include “robot_c_api.h”
#include “robot.h”

#ifdef __cplusplus
extern “C” {
#endif

// 因为我们将使用C++的编译方式,用g++编译器来编译 robot_c_api.cpp 这个文件,
// 所以在这个文件中我们可以用C++代码去定义函数 void Robot_sayHi(const char *name)(在函数中使用C++的类 Robot),
// 最后我们用 extern “C” 来告诉g++编译器,不要对 Robot_sayHi(const char *name) 函数进行name mangling
// 这样最终生成的动态链接库中,函数 Robot_sayHi(const char *name) 将生成 C 编译器的符号表示。

void Robot_sayHi(const char *name)
{
Robot robot(name);
robot.sayHi();
}

#ifdef __cplusplus
}
#endif


同样用编译C++代码的方式进行编译



g++ -fpic -shared robot_c_api.cpp -L. -lrobot -o librobot_c_api.so


![img](https://img-blog.csdnimg.cn/img_convert/a9bf3f83b223538447a450dd95067fa8.png)


现在我们有了一个动态链接库 **librobot\_c\_api.so,** 这个动态链接库提供了一个C函数 **Robot\_sayHi(const char \*name)**,我们可以在C程序中调用它了。


**main.c**



#include “robot_c_api.h”

int main()
{
Robot_sayHi(“Alice”);
Robot_sayHi(“Bob”);

return 0;

}


使用C程序的编译方式,用 **gcc** 对 **main.c** 进行编译



gcc main.c -L. -lrobot_c_api


![img](https://img-blog.csdnimg.cn/img_convert/30ec1c6a17680dae8950907f4365d1bb.png)


可以看到 **gcc** 编译出的函数符号和 **librobot\_capi.so**中 **g++** 编译器编译出的**函数符号一致**。这样最终在我们的C程序中可以正确的链接到动态库中的**Robot\_sayHi(const char \*name)** 函数。


![img](https://img-blog.csdnimg.cn/img_convert/8a90e7fe3780ff0ccc6e22111806c8bf.png)




---


![image-20220115133443894](https://img-blog.csdnimg.cn/img_convert/ccec9f43f502e608e983bbaa561a666e.png)


注意这个.c文件一定要用`g++`编译,不然识别不了c++的头文件,导致折腾了很久。



test:test.c
g++ -o $@ $^ -I ./mylib -L ./mylib -l robot_c_api

.PHONY:clean
clean:
rm -rf test



librobot_c_api.so:robot.o robot_c_api.o
g++ -shared -o $@ $^
robot_c_api.o:robot_c_api.cc
g++ -fPIC -c $<
robot.o:robot.cc
g++ -fPIC -c $<

.PHONY:clean
clean:
rm -rf *.o mylibrobot librobot_c_api.so librobot.so output

.PHONY:output
output:
mkdir -p ./output/include
mkdir -p ./output/lib
cp ./.so ./output/lib
cp ./
.h ./output/include



export LD_LIBRARY_PATH=/home/ycb/demo1/back-Cpp-C-extern/Cpp-C-extern/Cpp-C-extern/mylib


![image-20220115133129475](https://img-blog.csdnimg.cn/img_convert/b9eb1351c9e235b2edf7aec8a858cb50.png)


#### 5.4相关问题


* 下面两个函数能形成函数重载吗?有问题吗或者什么情况下会出问题?



void f(){
cout<<“f()”<<endl;
}
void f(int a=0){
cout<<“f(int a)”<<endl;
}
int main(){
f();//error:调用存在歧义
}


* C语言中为什么不能支持函数重载?
* C++中函数重载底层是怎么处理的?
* C++中能否将一个函数按照C的风格来编译?


### **6.引用**


#### 6.1引用概念



#include
using namespace std;
int main()
{
int a=1;
int& ra=a; //ra是a的引用,引用也就是别名。a再取了一个名称ra
int& raa=ra;
}


引用在物理空间上的意义:


![image-20210828154708086](https://gitee.com/yyyqwqcode/tupian/raw/master/img/image-20210828154708086.png)
引用就是给一个变量再取新的名字。编译器不会为引用变量开辟内存空间,**它和它引用的变量共用一块内存空间**


类型名& 引用变量名(对象名)=引用实体


#### 6.2引用特性


1. 引用必须**在定义时**初始化


	1. ```
	int a=1;
	int& b;///error
	
	```
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,不能发生变化

 

//1.引用必须在定义时必须初始化
int main(){
int a=10;
int &b;
}

 

//2.一个变量可以有多个引用
int main(){
int a =10;
int&b =a;
int&c =a;
int&d =b;
}

 

//3.引用一旦引用一个实体,再不能引用其他实体
int main(){

int a=10;
int &b=a;
int c=20;

b=c;///分析:这里是c变成了d的引用?还是d赋值给c(yes)

}



#### 6.3常引用


总结:


* **引用取别名时,变量访问权限可以缩小,不能放大。**
* **权限的放大和缩小规则:适用于引用和指针。不适用变量之间的赋值。**


##### 6.3.1权限的缩小与放大



int main()
{
int a=0;
int& b=a; //b的类型是int

const int a=0;
int&b = a;//error:编译不通过。原因:a是const,但是不能修改,b的类型是int,也就是可读可写,那么逻辑上就会产生矛盾。
const int& b=a;//right

int c=1;
int& d=c;
const int& e =c;行不行?可以->c是可读可写的,e变成别名是只读,逻辑上是可以的。

//变量之间赋值没有权限缩小和放大的关系,引用才有
const int ci =i ;
int x=ci;

return 0;
}



int main()
{
const int* cp1=&a;
int* p1=cp1;//error: const int* cp1表示cp1指向的内容不能更改。而int* p1=cp1如此赋值表明p1可以修改该块内存。逻辑错误。权限的放大。

int\* p2=&c;
const int\* cp2=p2; //权限缩小,ok

}



void f(int &x){
cout<<x<<endl;
}
int main(){
const int a=10;
const int &b=a;
f(a);//error:权限的放大。
f(b);
}



void f(const int& x){
cout<<x<<endl;
}
int main(){
const int a=10;
const int& b=a;
f(a);
f(b);
}


特别地,当涉及到类与对象的时候,对于`this`指针的问题。


类中的函数的隐藏`this`指针修饰对象和普通函数const参数对象直接调用的权限不对等



class A{
public:
double get_avg_score(){};
}
void fun(const A& a){
a.get_avg_score();/*会报错*/
}
void fun(const A& a) const{
a.get_avg_score();
}


在类中的函数后面加`const`对`this`指针进行修饰即可。




---


**权限的放大和缩小规则:适用于引用和指针。不适用变量之间的赋值。**


不用引用的话传参无所谓,只是对象之间的赋值。const对象拷贝给x。



void f(int x){
cout<<x<<endl;
}
int main(){
const int a=10;
const int &b =a;
f(a);
f(b);
}


##### 6.3.2产生右值的场景


类型转化,类型截断,类型提升函数返回值,函数传参,都会产生临时变量。



int main()
{
int i=0;
double db=i; //隐式类型转换
double& rdb=i;//error
float& rf=i;//error:和字节大小无关
//但是+const就可以
const double& rd=i;
const float& rf=i;
}


隐式类型转换的赋值是怎么产生的?


![image-20210828160751889](https://img-blog.csdnimg.cn/img_convert/f66471fe1570eff416bbf3c17edadbc4.png)


#### 6.4引用场景


##### 1、引用做参数



> 
> * 输出型参数
> * 提高效率
> 
> 
> 


回顾之前单链表的PushBack部分,我们要注意传递二级指针来处理原来指针变量的值。



void SLiPushBack(STLNode** pphead,SLTDataType x){
assert(pphead);

STLNode\* newnode=CreateSListNoded(x);
if(\*pphead==NULL){
    \*pphead=newnode;
    return;
}
else{
    STLNode\* tail=\*pphead;
    while(tail->next!=NULL) tail=tail->next;
    tail->next=newnode;
}

}
int main(){
SLTNode* plist=NULL;
SListPushBacn(&plist,1);
SListPushBacn(&plist,2);
SListPushBacn(&plist,3);
SListPushBacn(&plist,4);

SListPushBack(plist);
return 0;

}


有了引用之后,就可以省去一层二级指针。



int main(){

int a=10;
int& b=a;

int \*p1=&a;
int \*&p2=p1;

}



void SLiPushBack(STLNode*& pphead,SLTDataType x){
assert(pphead);

STLNode\* newnode=CreateSListNoded(x);
if(pphead==NULL){
    pphead=newnode;
    return;
}
else{
    STLNode\* tail=pphead;
    while(tail->next!=NULL) tail=tail->next;
    tail->next=newnode;
}

}
int main(){
SLTNode* plist=NULL;
SListPushBacn(&plist,1);
SListPushBacn(&plist,2);
SListPushBacn(&plist,3);
SListPushBacn(&plist,4);

SListPushBack(plist);
return 0;

}


再比如做C语言的题的时候给定接口的`int* returnSize`就是一个输出型参数。



void swap_c(int* p1,int *p2)
{
int tmp=*p1;
*p1=*p2;
*p2=tmp;
}
void swap_cpp(int &r1,int &r2)
{
int tmp=r1;
r1=r2;
r2=tmp;
}
int main()
{
int a=0;int b=1;
swap(&a,&b);
swap_cpp(a,b);
return 0;
}


前面说到引用定义的时候要初始化,这里引用定义的地方在传参。


##### 2、引用做返回值



> 
> 总结:
> 
> 
> 1. 凡是传值,不管是参数还是址,都会产生拷贝变量。传引用不会。
> 2. **一个函数要使用引用返回,返回变量出了这个函数的作用域还存在,就可以使用引用返回,否则不安全。**
> 	1. 全局变量、静态变量等
> 3. 函数使用引用返回的好处是什么
> 	1. 少创建拷贝一个临时对象,提高效率。
> 	2. 其实还有一个作用,以后再补充。
> 	3. 修改返回对象如operator[] (已补充–模板初阶模板类)
> 
> 
> 


先来回顾一下传值返回。


**所有的传值都会生成一个拷贝**



int Add(int a,int b)
{
int c=a+b;
return c;
}
int main(){
int ret=Add(1,2);
cout<<ret<<endl;
return 0;
}


![image-20211128151853981](https://img-blog.csdnimg.cn/img_convert/5f806d4a2a40dc6479ea34cfccf7a627.png)


我们可以看到调用`Add(int,int)`函数的过程`return c`的过程中,将计算出来的`c变量`的值存到了临时变量`%eax`寄存器中,然后再传给`main`函数中的`ret`变量。


临时变量存在哪里呢?


1. 如果`c`如果比较小(4 or 8),一般是寄存器充当临时变量。
2. 如果`c`比较大,临时变量放在调用Add函数的栈帧中。


**而传引用返回就是不会生成c的拷贝返回,直接返回c的引用**



int Add1(int a,int b)
{
int c=a+b;
return c;
}
int main()
{
const int& ret=Add1(1,2);//临时变量具有常性
Add1(3,4);
cout<<“Add1(1,2) is:”<<ret<<endl;
}



int& Add2(int a,int b)
{
int c=a+b;
return c;
}
int& Add2(int a,int b)
{
static int c=a+b;
return c;
}
int main()
{
int& ret=Add2(1,2);//ret就是c的别名。(实际上是c这块空间的别名).
//销毁不意味着清除,是没有使用权。
Add2(3,4);
cout<<“Add2(1,2) is:”<<ret<<endl;///ret输出为7了。引用返回是不安全的。
//说明如果返回变量c是一个局部变量时,引用返回是不安全的。
return 0;
}


![img]()
出了作用域还是返回已经销毁的栈帧(未有使用权)。不能保证原来的结果,其他函数能改这一块的。就会产生问题。


如何解决这个问题?


加static。



void test()
{
static int a=1;///第二次不执行
a++;
printf(“%d”,a);
}


此时c不在Add2的栈帧。


所以第二次Add(3,4)的时候static int c=a+b是不执行的。这份代码是只能是3。不过只有本函数才能改自己的c。



int Count1()//传值返回
{
static int n=0;
n++;
return n;//返回临时变量
}
int& Count2()//传引用返回
{
static int n=0;
n++;
return n;//没有额外空间
}
int main()
{
int& r1=Count1();//error:r1想成为临时变量的别名,因为临时变量具有常性。所以不行。需要加const
int& r2=Count2();//tmp相当于n的别名。r2相当于tmp的别名。
return 0;
}


![image-20210828194342670](https://img-blog.csdnimg.cn/img_convert/34149c8725a47909d4971f183888268e.png)


![image-20210828194442954](https://img-blog.csdnimg.cn/img_convert/63ca09f74c2cc512b2b848e4d99ba933.png)


#### 6.5传值,传引用的效率比较



#include
struct A{
int a[10000];
};
A a;
A TestFunc1() {return a;}
A& TestFunc2() { return a;}

void main()
{
size_t begin1=clock();

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
的结果,其他函数能改这一块的。就会产生问题。

如何解决这个问题?

加static。

void test()
{
    static int a=1;///第二次不执行
    a++;
    printf("%d",a);
}

此时c不在Add2的栈帧。

所以第二次Add(3,4)的时候static int c=a+b是不执行的。这份代码是只能是3。不过只有本函数才能改自己的c。

int Count1()//传值返回
{
    static int n=0;
    n++;
    return n;//返回临时变量
}
int& Count2()//传引用返回
{
	static int n=0;
    n++;
    return n;//没有额外空间
}
int main()
{
	int& r1=Count1();//error:r1想成为临时变量的别名,因为临时变量具有常性。所以不行。需要加const
    int& r2=Count2();//tmp相当于n的别名。r2相当于tmp的别名。
    return 0;
}

image-20210828194342670

image-20210828194442954

6.5传值,传引用的效率比较
#include<ctime>
struct A{
    int a[10000];
};
A a;
A TestFunc1() {return a;}
A& TestFunc2() { return a;}

void main()
{
	size_t begin1=clock();


**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)**
[外链图片转存中...(img-MIXu3Q1K-1713706693725)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值