引用不能引用数组
引用大小测试
class MyClass
{
char & a;
char & b;
char & c;//引用的本质是指针,直接sizeof引用,就是求引用的数据大小
//引用变量占据8个字节,平台为liunx X86_64
};
int main()
{
int num = 10;
int & rnum(num);
double db = 10.9;
double & rdb(db);//直接作用引用的变量
//测试引用变量所占的大小
std::cout << sizeof(rnum) << std::endl;//sizeof直接作用于引用的变量所以大小为该类型数据的值
std::cout << sizeof(rdb) << std::endl;
std::cout << sizeof(MyClass) << std::endl;
return 0;
}
out:
4
8
24
左值引用
返回栈中数据
局部基本数据类型
//该程序运行会产生段错误,原因是访问了已经释放的栈变量
int& mac()
{
int num=10;
return num;
}
int main()
{
//用基本数据来接收返回值
int i=mac();、
//用引用来接收返回值
int &p=mac();
cout<<p<<endl;
cout<<i<<endl;
return 0;
}
asm:#无关代码已略去
Dump of assembler code for function main():
...
0x0000555555554a3c <+8>: call 0x555555554955 <mac()>
0x0000555555554a41 <+13>: mov eax,DWORD PTR [rax]
0x0000555555554a43 <+15>: mov DWORD PTR [rbp-0xc],eax
0x0000555555554a46 <+18>: call 0x555555554955 <mac()>
=> 0x0000555555554a4b <+23>: mov QWORD PTR [rbp-0x8],rax
...
0x0000555555554aa4 <+112>: ret
End of assembler dump.
//实际gcc编译生成的汇编不是如此,此处是使用全局num生成的汇编,
Dump of assembler code for function mac():
0x0000555555554955 <+0>: push rbp
0x0000555555554956 <+1>: mov rbp,rsp
0x0000555555554959 <+4>: mov DWORD PTR [rip+0x2017d1],0xa # 0x555555756134 <num>
0x0000555555554963 <+14>: lea rax,[rip+0x2017ca] # 0x555555756134 <num>
=> 0x000055555555496a <+21>: pop rbp
0x000055555555496b <+22>: ret
End of assembler dump.
vs2015中反汇编mac
00F721F8 C7 45 F4 0A 00 00 00 mov dword ptr [num],0Ah
int i = (int)#
00F721FF 8D 45 F4 lea eax,[num]
00F72202 89 45 E8 mov dword ptr [i],eax
return num;
00F72205 8D 45 F4 lea eax,[num]
gdb中反汇编
Dump of assembler code for function mac():
0x00005555555549e8 <+0>: push rbp
0x00005555555549e9 <+1>: mov rbp,rsp
0x00005555555549ec <+4>: sub rsp,0x10
0x00005555555549f0 <+8>: mov rax,QWORD PTR fs:0x28
0x00005555555549f9 <+17>: mov QWORD PTR [rbp-0x8],rax
0x00005555555549fd <+21>: xor eax,eax
0x00005555555549ff <+23>: mov DWORD PTR [rbp-0xc],0xa
=> 0x0000555555554a06 <+30>: mov eax,0x0
0x0000555555554a0b <+35>: mov rdx,QWORD PTR [rbp-0x8]
0x0000555555554a0f <+39>: xor rdx,QWORD PTR fs:0x28
0x0000555555554a18 <+48>: je 0x555555554a1f <mac()+55>
0x0000555555554a1a <+50>: call 0x555555554850 <__stack_chk_fail@plt>
0x0000555555554a1f <+55>: leave
0x0000555555554a20 <+56>: ret
End of assembler dump.gdb中对栈进行了检查
用基本数据类型接收时:mac函数执行,将临时变量num的地址存入rax寄存器中作为返回值,函数返回后在main函数中取出该地址的值存入变量i中
用引用接收是:mac函数执行,将临时变量num的地址存入rax寄存器中作为返回值,函数返回后在main函数直接将该地址取出存入引用变量当中,之后一系列操作均通过引用变量中保存的地址值来完成,若之后调用一些函数执行,则有可能会覆盖之前mac函数的堆栈,导致数据错误,并且当mac函数执行结束返回后该调用堆栈在系统中已经处于回收状态,是不可用的。
局部类
先看一个不用引用作为函数返回值的函数
class Struct{
public:
int a,b,c,d;
int aa[10];
};
Struct Smac()
{
Struct test;
test.a=1;
test.b=2;
test.c=3;
test.d=4;
return test;
}
int main()
{
Struct p=Smac();
return 0;
}
函数返回时会进行内存复制,详细过程会在后续介绍
引用作为函数返回值
//程序是错误的
Struct& Smac()
{
Struct test;
test.a=1;
test.b=2;
test.c=3;
test.d=4;
return test;
}
int main()
{
Struct p=Smac();
Struct& pp=Smac();
return 0;
}
asm:
Dump of assembler code for function Smac():
0x00005555555547aa <+0>: push rbp
0x00005555555547ab <+1>: mov rbp,rsp
0x00005555555547ae <+4>: sub rsp,0x40
0x00005555555547b2 <+8>: mov rax,QWORD PTR fs:0x28
0x00005555555547bb <+17>: mov QWORD PTR [rbp-0x8],rax
0x00005555555547bf <+21>: xor eax,eax
0x00005555555547c1 <+23>: mov DWORD PTR [rbp-0x40],0x1
0x00005555555547c8 <+30>: mov DWORD PTR [rbp-0x3c],0x2
0x00005555555547cf <+37>: mov DWORD PTR [rbp-0x38],0x3
0x00005555555547d6 <+44>: mov DWORD PTR [rbp-0x34],0x4
0x00005555555547dd <+51>: mov eax,0x0//将0地址放入了返回值,访问时会出现异常,导致程序崩溃
0x00005555555547e2 <+56>: mov rdx,QWORD PTR [rbp-0x8]
0x00005555555547e6 <+60>: xor rdx,QWORD PTR fs:0x28
0x00005555555547ef <+69>: je 0x5555555547f6 <Smac()+76>
0x00005555555547f1 <+71>: call 0x555555554670 <__stack_chk_fail@plt>
=> 0x00005555555547f6 <+76>: leave
0x00005555555547f7 <+77>: ret
End of assembler dump.
返回堆中数据
仅返回引用
class Struct{
public:
int a,b,c,d;
int aa[10];
};
Struct& Smac()
{
Struct *test=new Struct;
test->a=1;
return *test;
}
int main()
{
Struct p=Smac();
Struct& pp=Smac();
return 0;
}
asm:
Dump of assembler code for function main():
...
=> 0x0000555555554828 <+8>: mov rax,QWORD PTR fs:0x28
0x0000555555554831 <+17>: mov QWORD PTR [rbp-0x8],rax
0x0000555555554835 <+21>: xor eax,eax
0x0000555555554837 <+23>: call 0x5555555547fa <Smac()>
0x000055555555483c <+28>: mov rcx,rax//得到分配变量的地址,而后进行一系列内存复制操作
0x000055555555483f <+31>: mov rax,QWORD PTR [rcx]
0x0000555555554842 <+34>: mov rdx,QWORD PTR [rcx+0x8]
0x0000555555554846 <+38>: mov QWORD PTR [rbp-0x40],rax
0x000055555555484a <+42>: mov QWORD PTR [rbp-0x38],rdx
0x000055555555484e <+46>: mov rax,QWORD PTR [rcx+0x10]
0x0000555555554852 <+50>: mov rdx,QWORD PTR [rcx+0x18]
0x0000555555554856 <+54>: mov QWORD PTR [rbp-0x30],rax
0x000055555555485a <+58>: mov QWORD PTR [rbp-0x28],rdx
0x000055555555485e <+62>: mov rax,QWORD PTR [rcx+0x20]
0x0000555555554862 <+66>: mov rdx,QWORD PTR [rcx+0x28]
0x0000555555554866 <+70>: mov QWORD PTR [rbp-0x20],rax
0x000055555555486a <+74>: mov QWORD PTR [rbp-0x18],rdx
0x000055555555486e <+78>: mov rax,QWORD PTR [rcx+0x30]
0x0000555555554872 <+82>: mov QWORD PTR [rbp-0x10],rax
0x0000555555554876 <+86>: call 0x5555555547fa <Smac()>
0x000055555555487b <+91>: mov QWORD PTR [rbp-0x48],rax
0x000055555555487f <+95>: mov eax,0x0
0x0000555555554884 <+100>: mov rdx,QWORD PTR [rbp-0x8]
0x0000555555554888 <+104>: xor rdx,QWORD PTR fs:0x28
0x0000555555554891 <+113>: je 0x555555554898 <main()+120>
0x0000555555554893 <+115>: call 0x5555555546c0 <__stack_chk_fail@plt>
0x0000555555554898 <+120>: leave
0x0000555555554899 <+121>: ret
End of assembler dump.
Dump of assembler code for function Smac():
=> 0x00005555555547fa <+0>: push rbp
0x00005555555547fb <+1>: mov rbp,rsp
0x00005555555547fe <+4>: sub rsp,0x10
0x0000555555554802 <+8>: mov edi,0x38
0x0000555555554807 <+13>: call 0x5555555546b0 <_Znwm@plt>
0x000055555555480c <+18>: mov QWORD PTR [rbp-0x8],rax
0x0000555555554810 <+22>: mov rax,QWORD PTR [rbp-0x8]
0x0000555555554814 <+26>: mov DWORD PTR [rax],0x1
0x000055555555481a <+32>: mov rax,QWORD PTR [rbp-0x8]
0x000055555555481e <+36>: leave
0x000055555555481f <+37>: ret
End of assembler dump.
当使用普通变量接收函数返回值时,传出分配变量的地址,而后使用该地址进行内存复制
当使用引用变量时,直接将传出的地址存入即可
指针引用
int main()
{
int i=0;
int *p=&i;
int*& pp=p;
int num=*pp;
return 0;
}
asm:
Dump of assembler code for function main():
0x00005555555547aa <+0>: push rbp
0x00005555555547ab <+1>: mov rbp,rsp
0x00005555555547ae <+4>: sub rsp,0x20
0x00005555555547b2 <+8>: mov rax,QWORD PTR fs:0x28
0x00005555555547bb <+17>: mov QWORD PTR [rbp-0x8],rax
0x00005555555547bf <+21>: xor eax,eax
0x00005555555547c1 <+23>: mov DWORD PTR [rbp-0x20],0x0
0x00005555555547c8 <+30>: lea rax,[rbp-0x20]
0x00005555555547cc <+34>: mov QWORD PTR [rbp-0x18],rax
=> 0x00005555555547d0 <+38>: lea rax,[rbp-0x18]
0x00005555555547d4 <+42>: mov QWORD PTR [rbp-0x10],rax
0x00005555555547d8 <+46>: mov rax,QWORD PTR [rbp-0x10]
0x00005555555547dc <+50>: mov rax,QWORD PTR [rax]
0x00005555555547df <+53>: mov eax,DWORD PTR [rax]
0x00005555555547e1 <+55>: mov DWORD PTR [rbp-0x1c],eax
0x00005555555547e4 <+58>: mov eax,0x0
0x00005555555547e9 <+63>: mov rdx,QWORD PTR [rbp-0x8]
0x00005555555547ed <+67>: xor rdx,QWORD PTR fs:0x28
0x00005555555547f6 <+76>: je 0x5555555547fd <main()+83>
0x00005555555547f8 <+78>: call 0x555555554670 <__stack_chk_fail@地址plt>
0x00005555555547fd <+83>: leave
0x00005555555547fe <+84>: ret
End of assembler dump.
指针的引用相当于一个二级指针,其内的值保存指针的地址
普通变量引用
int main()
{
int i=0;
int &p=i;
return 0;
}
asm:
0x00005555555547c8 <+30>: lea rax,[rbp-0x14] //i的地址
0x00005555555547cc <+34>: mov QWORD PTR [rbp-0x10],rax//将地址存入p中
普通变量的引用相当于一个一级指针,保存引用变量的地址
引用作为函数参数
int mac(int &i)
{
i=10;
return 0;
}
int main()
{
int num=10;
mac(num);
return 0;
}
asm:
Dump of assembler code for function main():
...
0x00005555555547da <+23>: mov DWORD PTR [rbp-0xc],0xa
0x00005555555547e1 <+30>: lea rax,[rbp-0xc]//获得参数地址
0x00005555555547e5 <+34>: mov rdi,rax
0x00005555555547e8 <+37>: call 0x5555555547aa <mac(int&)>
...
0x0000555555554807 <+68>: ret
End of assembler dump.
Dump of assembler code for function mac(int&):
...
0x00005555555547ae <+4>: mov QWORD PTR [rbp-0x8],rdi
0x00005555555547b2 <+8>: mov rax,QWORD PTR [rbp-0x8]
0x00005555555547b6 <+12>: mov DWORD PTR [rax],0xa
...
0x00005555555547c2 <+24>: ret
End of assembler dump.
传递参数时将变量地址保存到rdi寄存器中传参
右值引用
int main()
{
int num=10;
int *&&p=#
return 0;
}
asm:
Dump of assembler code for function main():
...
0x00005555555547c1 <+23>: mov DWORD PTR [rbp-0x1c],0xa
0x00005555555547c8 <+30>: lea rax,[rbp-0x1c]//&num
0x00005555555547cc <+34>: mov QWORD PTR [rbp-0x18],rax
0x00005555555547d0 <+38>: lea rax,[rbp-0x18]
0x00005555555547d4 <+42>: mov QWORD PTR [rbp-0x10],rax
...
0x00005555555547f2 <+72>: ret
End of assembler dump.
(gdb) p p
$21 = (int *&&) @0x7fffffffddd8: 0x7fffffffddd4
cout<<p;输出为0x7fffffffddd4
右值引用,好比一个二级指针,但不同的是访问p可以直接获得num的地址,因而右值引用保存的是一个地址,为右值,不可更改.右值引用常用于类之间的拷贝、移动;使用右值引用可以减少重复内存拷贝,传递函数指针、普通函数参数等.如std::move(T&& t)).
复杂引用
函数指针的引用
作为函数返回值
int(*& fun(int (*&rp)(int,int)))(int, int)//最后的(int, int)为返回类型
返回一个函数指针的引用,函数指针指向的函数类型为int(&)(int,int),也是一个返回函数指针引用的函数,类型为int(&)(int,int)
作为函数参数
void fun(int(* & rp)(int,int))
一个int(*)(int,int)型指针的引用作为函数参数的函数
int (*&rp)(int,int))