C++深度解析教程笔记3
本文学习自狄泰软件学院 唐佐林老师的 C++深度解析教程,图片全部来源于课程PPT,仅用于个人学习记录
第5课 - 引用的本质分析
实验-const引用
#include <stdio.h>
void Example()
{
printf("Example:\n");
int a = 4;
const int& b = a;
int* p = (int*)&b;
// b = 5;
/*
E:\test>c++ 5-1.cpp
5-1.cpp: In function 'void Example()':
5-1.cpp:11:9: error: assignment of read-only reference 'b'
b = 5;
*/
*p = 5;
printf("a = %d\n", a);
printf("b = %d\n", b);
// 注释*p = 5;
//a = 4
//b = 4
//不注释*p = 5;
//a = 5
//b = 5
}
void Demo()
{
printf("Demo:\n");
const int& c = 1;
int* p = (int*)&c;
//c = 5;
/*
E:\test>c++ 5-1.cpp
5-1.cpp: In function 'void Demo()':
5-1.cpp:33:9: error: assignment of read-only reference 'c'
c = 5;
*/
//*p = 5;
printf("c = %d\n", c);
// //*p = 5; output :c = 1
// *p = 5; output :c = 5
}
int main(int argc, char *argv[])
{
Example();
printf("\n");
Demo();
return 0;
}
实验-引用的本质
#include <stdio.h>
struct TRef
{
char& r;
};
int main(int argc, char *argv[])
{
char c = 'c';
char& rc = c;
TRef ref = { c };
printf("sizeof(char&) = %d\n", sizeof(char&));
printf("sizeof(rc) = %d\n", sizeof(rc));
printf("sizeof(TRef) = %d\n", sizeof(TRef));
printf("sizeof(ref.r) = %d\n", sizeof(ref.r));
return 0;
}
/*
cmd g++:
sizeof(char&) = 1
sizeof(rc) = 1
sizeof(TRef) = 8
sizeof(ref.r) = 1
codeblock:
sizeof(char&) = 1
sizeof(rc) = 1
sizeof(TRef) = 4
sizeof(ref.r) = 1
*/
void f(int & a)
{
a=5;}
等价于
void f(int * const a)
{
*a=5;
}
实验-引用所占空间大小
#include <stdio.h>
struct TRef
{
char* before;
char& ref;
char* after;
};
int main(int argc, char* argv[])
{
char a = 'a';
char& b = a;
char c = 'c';
TRef r = {&a, b, &c};
printf("sizeof(r) = %d\n", sizeof(r)); //4+1+4 X
printf("sizeof(r.before) = %d\n", sizeof(r.before));//4 ?
printf("sizeof(r.after) = %d\n", sizeof(r.after));//4?
printf("&r.before = %p\n", &r.before);//
printf("&r.after = %p\n", &r.after);//
printf("r.before = %p\n", r.before);//4
printf("r.after = %p\n", r.after);//
return 0;
}
/*
g++ cmd:
sizeof(r) = 24
sizeof(r.before) = 8
sizeof(r.after) = 8
&r.before = 000000000061FDF0
&r.after = 000000000061FE00
r.before = 000000000061FE17
r.after = 000000000061FE16
DF0 与E00 差16 ;sizeof(r.before) = 8,说明引用占的存储空间为8个字节
24-8-8=8;说明引用占的存储空间为8个字节
codeblock(32):
sizeof(r) = 12
sizeof(r.before) = 4
sizeof(r.after) = 4
&r.before = 0060FEEC
&r.after = 0060FEF4
r.before = 0060FEFB
r.after = 0060FEFA
EC 与F4 差8 ;sizeof(r.before) = 4,说明引用占的存储空间为4个字节
12-4-4=4;说明引用占的存储空间为4个字节
*/
vs2010反汇编
char a = 'a';
00E836DE mov byte ptr [a],61h //将97放到名称为a的一个字节的内存空间
char& b = a;
00E836E2 lea eax,[a] //将a的地址取出,放到eax寄存器中
00E836E5 mov dword ptr [b],eax //将eax中的内容,放入b标识符对应的内存空间 大小为4个字节(dword)
实验-局部变量的引用
#include <stdio.h>
//返回引用,局部变量
// warning: reference to local variable 'd' returned [-Wreturn-local-addr]
// int d = 0;
int& demo()
{
int d = 0;
printf("demo: d = %d\n", d);
return d;
}
//返回引用,静态变量
int& func()//int * const
{
static int s = 0;
printf("func: s = %d\n", s);
return s;
}
int main(int argc, char* argv[])
{
int& rd = demo();
int& rs = func();
printf("\n");
printf("main: rd = %d\n", rd);
printf("main: rs = %d\n", rs);
printf("\n");
rd = 10;
rs = 11;
demo();
func();
printf("\n");
printf("main: rd = %d\n", rd);
printf("main: rs = %d\n", rs);
printf("\n");
return 0;
}
/*
demo: d = 0
func: s = 0
main: rd = 1980251346 //野指针
main: rs = 0
demo: d = 0
func: s = 11
main: rd = 1980251346 //rd代表的内存空间无意义
main: rs = 11
*/
小结
引用作为变量别名而存在旨在代替指针
const引用可以使得变量具有只读属性
引用在编译器内部使用指针常量实现
引用的最终本质为为指针
引用可以尽可能的避开内存错误
第6课 - 内联函数分析
实验-内联函数
int c = func(++a, b);
001536EC mov eax,dword ptr [a]
001536EF add eax,1
001536F2 mov dword ptr [a],eax
001536F5 mov ecx,dword ptr [b]
001536F8 push ecx
001536F9 mov edx,dword ptr [a]
001536FC push edx
001536FD call func (1511DBh) //编译器未允许内联请求
00153702 add esp,8
00153705 mov dword ptr [c],eax
设置允许编译器内联
实验-forceinline
//vs2010
#include <stdio.h>
__forceinline //vs2010 设置内联函数扩展
//__attribute__((always_inline))//gcc
//inline 与 __forceinline作用一样
int add_inline(int n);
int main(int argc, char *argv[])
{
int r = add_inline(10);
printf(" r = %d\n", r);
return 0;
}
inline int add_inline(int n)
{
int ret = 0;
for(int i=0; i<n; i++)
{
ret += i;
}
return ret;
}
int c = func(++a, b);
00271036 mov eax,dword ptr [a]
00271039 add eax,1
0027103C mov dword ptr [a],eax
0027103F mov ecx,dword ptr [a]
00271042 cmp ecx,dword ptr [b]
00271045 jge main+3Fh (27104Fh)
00271047 mov edx,dword ptr [a]
0027104A mov dword ptr [ebp-10h],edx
0027104D jmp main+45h (271055h)
0027104F mov eax,dword ptr [b]
00271052 mov dword ptr [ebp-10h],eax
00271055 mov ecx,dword ptr [ebp-10h]
00271058 mov dword ptr [c],ecx
//内联不成功
int r = add_inline(10);
0010101C push 0Ah
0010101E call add_inline (10100Ah)
00101023 add esp,4
00101026 mov dword ptr [r],eax
//内联成功
int r = add_inline(10);
0026102C mov dword ptr [ebp-8],0
00261033 mov dword ptr [ebp-0Ch],0
0026103A jmp main+35h (261045h)
0026103C mov eax,dword ptr [ebp-0Ch]
0026103F add eax,1
00261042 mov dword ptr [ebp-0Ch],eax
00261045 cmp dword ptr [ebp-0Ch],0Ah
00261049 jge main+46h (261056h)
0026104B mov ecx,dword ptr [ebp-8]
0026104E add ecx,dword ptr [ebp-0Ch]
00261051 mov dword ptr [ebp-8],ecx
00261054 jmp main+2Ch (26103Ch)
00261056 mov edx,dword ptr [ebp-8]
00261059 mov dword ptr [r],edx