当前位置:   article > 正文

C++挖掘程序本质(第一章C++基础)李明杰-M了个J 配套教材_m了个j c++

m了个j c++

目录

前言

由于在CSDN上无法看到markdown的目录,有需要额小伙伴可以联系我,附送本文的 .md文件,可以再本地typora上更加方便的学习
这篇文章和 汇编入门基础(点此链接) 为想结合内容,请大家在学习时可以同时参考

1.C++基础知识

1.1 关于C++

1.1.1 C++的源文件拓展名是:cpp(c plus plus的简称)
1.1.2 C++程序的入口是main函数(函数即方法,一个意思)
1.1.3 C++完全兼容C语言的语法,很久以前,C++叫做C with classes
1.1.4 C++发展史
在这里插入图片描述

1.2 第一个C++程序 hello world

#include <iostream>
using namespace std;

int main() {
	cout << "hello world!" << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.3 cin、cout、endl 的使用

1.3.1 C++中常使用cin、cout进行控制台的输入、输出
1.3.2 使用cin cout 需要依赖#include 和using namespace std;
1.3.3 cin用右移运算符>>,cout用的是左移运算符<<
1.3.4 endl 是换行的意思

2.函数重载 - Overload

2.1 规则

2.1.1 函数名相同
2.1.2 参数个数不同、参数类型不同、参数顺序不同

#include <iostream>
using namespace std;

int sum(int v1, int v2) {
	return v1 + v2;
}
int sum(int v1, int v2, int v3) {
	return v1 + v2 + v3;
}
int main() {
	cout << sum(10, 20) << endl;
	cout << sum(10, 20,30) << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.2 注意

2.2.1 返回值类型与函数重载无关
2.2.2 调用函数时,实参的隐式类型转换可能会产生二义性
注意: C语言不支持函数重载、C++支持函数重载

2.3 本质

采用了name mangling或者叫做name decoration技术
重载时会生成多个不同的函数名,不同编译器(MSVC、g++)有不同的生成规则
通过IDA打开[vs 下 release 禁止优化] 可以看到

release下会进行优化(可以禁止)
debug 下可以查询所有反汇编
在这里插入图片描述

3.默认参数

3.1 C++允许函数设置默认参数,在调用时可以根据情况省略实参

规则如下:

#include <iostream>
using namespace std;

int sum(int v1 , int v2 = 6) {
	return v1 + v2;
}

int main() {
	cout << sum(10) << endl;
	cout << sum(10, 20) << endl;
	return 0;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
3.1.1 默认参数只能按照右到左的顺序

如下面代码写法错误,v2的赋值不能缺失

int sum(int v1 = 10, int v2 ) { //错!!!!
	return v1 + v2;  
}
  • 1
  • 2
  • 3
3.1.2 如果函数同时有声明、实现,默认参数只能放在函数声明中
#include <iostream>
using namespace std;
//声明
int sum(int v1, int v2 = 6);

int main() {
	cout << sum(10) << endl;
	cout << sum(10, 20) << endl;
	return 0;

}
//实现
int sum(int v1, int v2) {
	return v1 + v2;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
3.1.3 默认参数的值可以是常量、全局符号(全局变量、函数名)

1) 默认参数值是全局变量

#include <iostream>
using namespace std;
int age = 20;
int sum(int v1, int v2 = age) {
	return v1 + v2;
}
int main() {
	cout << sum(10) << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2) 指向函数的指针调用函数
(*p)(int) :(*p)是指向函数的指针 (int)为参数类型

#include <iostream>
using namespace std;
int age = 20;
void  test(int a) {
	cout << "test(int)" << a << endl;
}

int main() {
	void (*p)(int) = test;
	(*p)(age);
	p(age);
	return 0;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可见 (*p)(age); 和 p(age); 是一样的

	(*p)(age);
00111D0F 8B F4                mov         esi,esp  
00111D11 A1 00 C0 11 00       mov         eax,dword ptr ds:[0011C000h]  
00111D16 50                   push        eax  
00111D17 FF 55 F8             call        dword ptr [ebp-8]  
00111D1A 83 C4 04             add         esp,4  
00111D1D 3B F4                cmp         esi,esp  
00111D1F E8 66 F5 FF FF       call        0011128A  
	p(age);
00111D24 8B F4                mov         esi,esp  
00111D26 A1 00 C0 11 00       mov         eax,dword ptr ds:[0011C000h]  
00111D2B 50                   push        eax  
00111D2C FF 55 F8             call        dword ptr [ebp-8]  
00111D2F 83 C4 04             add         esp,4  
00111D32 3B F4                cmp         esi,esp  
00111D34 E8 51 F5 FF FF       call        0011128A
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3) 默认参数值是函数名

#include <iostream>
using namespace std;
int age = 20;

void  test(int a) {
	cout << "test(int):" << a << endl;
}

void func(int v1, void(*p)(int) = test) {
	p(v1);
}

int main() {
	func(30);
	return 0;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3.2 二义性

3.2.1 函数重载、默认参数可能会产生冲突、二义性(建议优先选择使用默认参数)

在这里插入图片描述

4. extern “C”

4.1 extern “C” 作用

被 extern “C” 修饰的代码会按照C语言的方式去编译

4.2 C语言不支持重载

4.3 俩种写法

extern "C" void func() {
}
extern "C" void func(int v) {
}
  • 1
  • 2
  • 3
  • 4
extern "C" {
	void func() {
	}
	void func(int v) {
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.4 有声明和实现的函数 与 extern “C”

4.4.1 如果函数同时有声明和实现,要让函数声明被 extern "C"修饰,函数实现可以不修饰
#include <iostream>
using namespace std;
// 函数声明
void func();
extern "C" void func(int v);

int main() {
	return 0;
}
// 函数实现
void func() {
}
void func(int v) {
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.5 C、C++ 混合开发

4.5.1 由于C、C++编译规则不同,在C、C++混合开发时,可能会经常出现以下操作

C++在调用C语言API 时,需要使用extern “C” 修饰C语言的函数声明

在这里插入图片描述
在C++ 中引用C文件,声明时需要加上extern “C”,否则报错
在这里插入图片描述

4.5.2 #define __cplusplus

只要在C++文件中,默认定义宏 #define __cplusplus (一般不可见)
可以用这个宏来判断是否为C++文件

1)math.c

#include "math.h"
//include 是为了在.c文件中
//调用自己不报错
int sum(int v1, int v2) {
	return v1 + v2;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2)main.cpp

#include <iostream>
using namespace std;

#include "math.h"
extern "C" {
	void other();
}

int main() {
	other();
	cout << sum(10, 20) << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3)math.h


#ifdef __cplusplus
extern  "C" {
#endif // __cplusplus

int sum(int v1, int v2);

#ifdef __cplusplus
}
#endif // __cplusplus
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

4)math.h

#include "math.h"
#include <stdio.h>
void other() {
	int a = sum(10, 99);
	printf("a= %d\n",a);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

4.4.3 防止头文件重复包含

我们经常使用 #ifndef、#define、#endif 来防止头文件的内容被重复包含

#ifndef __MATH_H
#define __MATH_H

#ifdef __cplusplus
extern  "C" {
#endif // __cplusplus

int sum(int v1, int v2);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // __MATH_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
4.4.4 #pragma once

#pragma once 可以防止整个文件的内容被重复包含
#ifndef、#define、#endif 与 #pragma once 区别 :
1)#ifndef、#define、#endif 受C\C++标准的支持,不收编译器的任何限制
2)有些编译器不支持#pragma once (较老编译器不支持,如GCC 3.4版本之前),兼容性不够好
3)#ifndef、#define、#endif 可以针对一个文件中的部分代码,而#pragma once只能针对整个文件

5.内联函数- inline function

5.1 inline 修饰符

5.1.1 使用 inline 修饰函数的声明或者实现 ,可以使其变成内联函数
5.1.2 建议声明和实现都增加 inline 修饰

5.2 特点

5.2.1 编译器会将函数调用直接展开为函数体代码

#include <iostream>
using namespace std;

inline int sum;
inline int sum(int v1, int v2) {
	return v1 + v2;
}

int main() {
	int c = sum(10, 20);
    //	等价于
	//int c =  v1 + v2;
    int c =  10 + 20;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

5.2.2 如果不使用 inline 函数,被调用 func的函数体只会保留一份 (即:cout << “func()” << endl;)

#include <iostream>
using namespace std;

void func() {
	cout << "func()" << endl;
}

int main() {
	func();
    func();
	func();
    func();
    
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

5.2.3 可以减少函数调用的开销(不存在函数调用,程序直接运行函数体内容)

5.2.4 会增大代码体积

注意

1)尽量不要内联超过10行代码的函数

2)有些函数即使声明为inline 也不一定会被编译器内联,比如:递归函数

5.3 什么时候使用内联函数?

5.3.1 函数代码体积不大

5.3.2 频繁调用的函数

#include <iostream>
using namespace std;

//====================  声明	====================
void func();
inline int sum(int v1, int v2);

//====================  实现	====================
//开辟 栈空间
void func() {
	cout << "func()" << endl;
}
//回收 栈空间

inline int sum(int v1, int v2) {
	return v1 + v2;
}

int main() {
	func();
	int c = sum(10, 20);
	cout << c << endl;
	getchar();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

5.4 VS窥探内联的本质

5.4.1 如果 inline 内联生效后,在反汇编中应该看不到定义的函数(如:5.4.3 内联的效果)
5.4.2 如何在 VS2019 中进行设置

在这里插入图片描述
在这里插入图片描述

5.4.3 内联的效果
--- E:\C++-程序\05-内联函数\05-内联函数\main.cpp -----------------------------------------
	func();
00291000  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (029305Ch)]  
00291006  push        offset std::endl<char,std::char_traits<char> > (0291320h)  
0029100B  call        std::operator<<<std::char_traits<char> > (0291100h)  
00291010  mov         ecx,eax  
00291012  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0293038h)]  
	int c = sum(10, 20);
	cout << c << endl;
00291018  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (029305Ch)]  
0029101E  push        offset std::endl<char,std::char_traits<char> > (0291320h)  
00291023  push        1Eh  
00291025  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0293034h)]  
0029102B  mov         ecx,eax  
0029102D  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0293038h)]  
	getchar();
00291033  call        dword ptr [__imp__getchar (02930F0h)]  
	return 0;
00291039  xor         eax,eax  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
5.4.4 非内联的效果
	func();
01052078  call        func (01051424h)  
	int c = sum(10, 20);
0105207D  push        14h  
0105207F  push        0Ah  
01052081  call        sum (01051429h)  
01052086  add         esp,8  
01052089  mov         dword ptr [c],eax  
	cout << c << endl;
0105208C  mov         esi,esp  
0105208E  push        offset std::endl<char,std::char_traits<char> > (010512B7h)  
01052093  mov         edi,esp  
01052095  mov         eax,dword ptr [c]  
01052098  push        eax  
01052099  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0105D0D8h)]  
0105209F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0105D0A0h)]  
010520A5  cmp         edi,esp  
	cout << c << endl;
010520A7  call        __RTC_CheckEsp (0105128Fh)  
010520AC  mov         ecx,eax  
010520AE  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0105D0A4h)]  
010520B4  cmp         esi,esp  
010520B6  call        __RTC_CheckEsp (0105128Fh)  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

5.5.内联函数与宏

5.5.1 相同点:内联函数和宏都能减少函数调用的开销
5.5.2 对比宏,内联函数多了语法检测和函数特性

而且宏会将普通的运算改变

#include <iostream>
using namespace std;

inline int sum(int v) {
	 return v  + v ;
}
//定义宏
#define add(v)(v + v)

int main() {
	int a = 10;
	int b = 10;
	int c = sum(++a);
	int d = add(++b);
	cout << c << endl;
	cout << d << endl;
	getchar();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

输出结果 :22 24

可以看出宏将++a 操作复杂化了

5.6 表达式

5.6.1 表达式性质仅为 C++特性,C语言没有此性质
5.6.2 表达式实例1
#include <iostream>
using namespace std;

//表达式
int main() {
	int a = 1;
	int b = 2;
    
	(a = b) = 4;
    
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	getchar();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

输出结果:a=4
b=12

5.6.3 表达式实例2
#include <iostream>
using namespace std;

//表达式
int main() {
	int a = 1;
	int b = 2;
    
	(a > b ? a : b) = 10;
    
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	getchar();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

输出结果:a=1
b=10

6.常量 - const

6.1 C++ 中的指针

6.1.1 回忆一下指针
#include <iostream>
using namespace std;
struct Date {
	int year;
	int month;
	int day;
};

int main() {
	Date d = { 2011,2,1 };
	Date d2 = { 2013,9,2 };
	//p指针指向d结构体
	Date* p = &d;
	//指向结构体的指针,访问结构体的成员使用 ->  (语法糖)
	p->day = 99;
  //*p 就是结构体即为d 等价于 d.month =12;
	(*p).month = 12;
	//结构体本身,访问结构体的成员使用 .  (语法糖)
	cout << d.day << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

6.2 const是什么?

const是常量的意思,被其修饰的变量不可修改

如果修饰的是类、结构体(的指针),其成员不可以更改

6.1.1 没有被 const 修饰 的结构体
#include <iostream>
using namespace std;
struct Date {
	int year;
	int month;
	int day;
};

int main() {
	Date d = { 2011,2,1 };
	Date d2 = { 2013,9,2 };
	d = d2;
	d.year = 2022;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
6.1.2 被 const 修饰 的结构体

结构体不能改

结构体成员也不能改

#include <iostream>
using namespace std;
struct Date {
	int year;
	int month;
	int day;
};

int main() {
	const Date d = { 2011,2,1 };
	Date d2 = { 2013,9,2 };
	d = d2; 			// 报错	结构体不能改->不能将d指向d2
	d.year = 2022;// 报错	结构体成员也不能改->不能操作d的成员
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
6.1.3 用const 修饰的 指针变量
const Date* p = &d 后同样等价于结构体指针变量
#include <iostream>
using namespace std;

int main() {
	Date d = { 2011,2,1 };
	Date d2 = { 2013,9,2 };

	//p指针指向d结构体
	const Date* p = &d;
	p->day = 99;			// 报错
	(*p).month = 12;	// 报错
	*p = d2;					// 报错
	cout << d.day << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

6.3 const 修饰指针(p) 与 指针指向的存储空间(*p) 的应用(区别)

6.3.1 以下5个指针分别是什么含义?
#include <iostream>
using namespace std;

int main() {
	int age = 10;
	int height = 20;

	const int *p1 = &age;
	int const* p2 = &age;
	int* const p3 = &age;
	const int* const p4 = &age;
	int const* const p5 = &age;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

结论:const修饰的是其右边的内容

6.3.2 首先看一下
#include <iostream>
using namespace std;

int main() {
	int age = 10;
	int height = 30;

	int* const p3 = &age;
  // p3所指向的存储空间的值改为20(当前p3指向 age的地址)
	*p3 = 20;	// age = 20;
  // 代码含义: 将height的地址 通过 &height赋值给 p3,此时p3指向height的地址
	p3 = &height; //==================此行代码报错,原因 p3是常量
	// p3所指向的存储空间的值改为40(当前p3指向 height的地址)
  *p3 = 40;	// height = 40;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
6.3.3 五种const 修饰的归类 ☆☆☆☆☆
1)const 修饰指针指向的存储空间
2)const 修饰指针
3)const 修饰指针 + 指针指向的存储空间
#include <iostream>
using namespace std;

int main() {
	int age = 10;
	int height = 20;
	// p1(指针)不是常量,*p1(指向的存储空间)是常量
  // p2(指针)不是常量,*p2(指向的存储空间)是常量
	const int *p1 = &age;
	int const* p2 = &age;
  // p3(指针)是常量,*p3(指向的存储空间)不是常量
	int* const p3 = &age;
  // p4(指针)是常量,*p4(指向的存储空间)也是常量
  // p5(指针)是常量,*p5(指向的存储空间)也是常量
	const int* const p4 = &age;
	int const* const p5 = &age;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
6.3.4 再次理解 const修饰 指针 和 指针指向的存储空间

const 修饰的是 *pstu1(指针指向的存储空间),禁止pstu1指针去修改它所指向的存储空间

#include <iostream>
using namespace std;

struct Student { int age; };

int main() {
	Student stu1 = { 10 };
	Student stu2 = { 20 };
	//const 修饰的是* pstu1,禁止pstu1指针去修改它所指向的存储空间
	const Student* pstu1 = &stu1;
	*pstu1 = stu2;			// 报错
	(*pstu1).age = 20;	// 报错
	pstu1->age = 30;		// 报错
	pstu1 = &stu2; //正确
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

const 修饰的是 pstu2(指针),禁止pstu2指针去重新指向新的地址

#include <iostream>
using namespace std;

struct Student { int age; };

int main() {
	Student stu1 = { 10 };
	Student stu2 = { 20 };
	//const 修饰的是* pstu1,禁止pstu1指针去修改它所指向的存储空间
	Student* const pstu2 = &stu2;
	*pstu2 = stu2;		// 正确
	(*pstu2).age = 20;	// 正确
	pstu2->age = 30;	// 正确
	pstu2 = &stu2; //报错
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

7.引用 - Reference

7.1 引用Reference 的本质是什么?

引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针

#include <iostream>
using namespace std;

void swap(int v1,int v2) {
	int tmp = v1;
	v1 = v2;
	v2 = tmp;
}
int main() {
	int a = 10;
	int b = 20;
	swap(a, b);
	cout << "a="<<a << ",b=" << b << endl;
	getchar();
	return 0;
}

//输出:a=10,b=20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

使用 指针(Pointer) 定义变量v1和v2

#include <iostream>
using namespace std;

void swap(int * v1,int *v2) {
	int tmp = *v1;
	*v1 = *v2;
	*v2 = tmp;
}
int main() {
	int a = 10;
	int b = 20;
	swap(&a, &b);
	cout << "a="<<a << ",b=" << b << endl;
	getchar();
	return 0;
}
//输出:a=20,b=10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

使用 引用(Reference) 定义变量v1和v2

#include <iostream>
using namespace std;

void swap(int& v1, int& v2) {
	int tmp = v1;
	v1 = v2;
	v2 = tmp;
}

int main() {
	int a = 10;
	int b = 20;
	swap(a, b);
	cout << "a=" << a << ",b=" << b << endl;
	getchar();
	return 0;
}
//输出:a=20,b=10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
#include <iostream>
using namespace std;

void swap(int& v1, int& v2) {
	int tmp = v1;
	v1 = v2;
	v2 = tmp;
}

int main() {
	int a = 10;
	int b = 20;
	swap(a, b);
	cout << "a=" << a << ",b=" << b << endl;
	int c = 30;
	int d = 40;
	swap(c, d);
	cout << "c=" << c << ",d=" <<d << endl;
	getchar();
	return 0;
}
//输出:	a=20,b=10
				c=40,d=30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在定义“引用变量”时就赋值-即指向谁(否则 vs 中报错)

int age =10;
int &refAge = age; // 定义时 赋值
  • 1
  • 2
#include <iostream>
using namespace std;

int main() {
	int a = 10;
	int b = 20;
	int& refA = a;
	cout << "当前 refA=" << refA << endl;
	refA = b; // 实际上是将 b的值20 赋值给引用变量 refA
	cout << "当前 refA=" << refA << endl;
	getchar();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

7.2 一个引用占用一个指针的大小

7.2.1 * p 指针间接修改 age 的值
#include <iostream>
using namespace std;

int main() {
	int age = 10;
	int* p = &age;
	*p = 5;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

C++ 对应的汇编

     5: 	int age = 10;
008917E2  mov         dword ptr [ebp-0Ch],0Ah  
     6: 	int* p = &age;
008917E9  lea         eax,[ebp-0Ch]  
008917EC  mov         dword ptr [ebp-18h],eax  
     7: 	*p = 5;
008917EF  mov         eax,dword ptr [ebp-18h]  
008917F2  mov         dword ptr [eax],5 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

7.2.2 &ref 引用间接修改age 的值
#include <iostream>
using namespace std;

int main() {
	int age = 10;
	int &ref  =  age;
	ref = 5;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
     7: 	int age = 10;
013E17E2  mov         dword ptr [ebp-0Ch],0Ah  
     8: 	int &ref  =  age;
013E17E9  lea         eax,[ebp-0Ch]  
013E17EC  mov         dword ptr [ebp-18h],eax  
     9: 	ref = 5;
013E17EF  mov         eax,dword ptr [ebp-18h]  
013E17F2  mov         dword ptr [eax],5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

7.3 C 指针 与C++ 引用区别

在C语言中,使用指针(Pointer)可以间接获取、修改某个变量的值 (在C++可用)
在C++语言中,使用引用(Reference)可以起到跟指针类似的功能

7.4 注意点

7.4.1 引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等、都可以有引用)

1)结构体引用

#include <iostream>
using namespace std;

struct Date
{
	int year;
	int month;
	int day; 
};
int main() {
	//结构体引用
	Date d = { 2011,2,3 };
	Date& ref = d;
	ref.day = 18;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2)指针的引用

#include <iostream>
using namespace std;

int main() {
	//指针的引用
	int age = 10;
	int* p = &age;
	int*& ref = p;
	* ref = 30;
	int height = 30;
	ref = &height;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3)用于指向数组的引用(指针)

#include <iostream>
using namespace std;

int main() {
	int array[] = { 1,2,3 };
	//指针数组,数组里面可以存放3个int*
	int* arr[3];
	//用于指向数组的指针
	int(*arr2)[3];
	int(&refArr)[3] = array;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
7.4.2 对引用做计算,就是对引用所指向的变量做计算
7.4.3 在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”
7.4.4 可以利用引用初始化另一个引用,相当于某个变量的多个别名
7.4.5 不存在【引用的引用、指向引用的指针、引用数组】

引用存在的价值之一:比指针更安全、函数返回值可以被赋值

7.5 常引用(Const Reference)

7.5.1 什么是常引用?

引用可以被const修饰,这样就无法通过引用修改数据了,可以称为常引用
const必须写在&符号的左边,才能算是常引用
常引用:可以访问,不可以修改

应用场景:
函数 test中p只可以访问外面的值,不可以改变外面的值

#include <iostream>
using namespace std;

void test(const int& p) {
	// p = 30;  不可改变
	cout << p << endl;
}
int main() {
	int age = 20;
	test(age)
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

等价写法

	int age = 10;
	int const& r = age;
	const int & r = age;
  • 1
  • 2
  • 3
7.5.2 const &ref 与 & const ref

1) ref 不能修改指向,但可以通过ref间接修改所指向的变量

俩种写法一样 ref 本来就不能修改

int& const ref = age;
int&  ref = age;
  • 1
  • 2
#include <iostream>
using namespace std;

struct Date
{
	int year;
	int month;
	int day; 
};
void test(const int& p) {
	p = 30;
	cout << p << endl;
}
int main() {

	int age = 10;
	//ref 不能修改指向,但可以通过ref间接修改所指向的变量
	//引用本身不能修改指向
	int& const ref = age;
	int&  ref = age;
	ref = 30;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2)ref 不能修改指向,不可以通过ref间接修改所指向的变量严格限制

int const& ref = age;
  • 1

7.6 const常引用的特点

7.6.1 可以指向临时数据(常量、表达式、函数返回值等)

1)常量

#include <iostream>
using namespace std;

int main() {
	int age = 10;
	int& ref =10; //报错 : 不可以,因为会被修改
	const int& ref = 10;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2)表达式

#include <iostream>
using namespace std;

int main() {
	int a = 1;
	int b = 2;
	int& ref = a + b;//报错 : 不可以
	const int& ref = a + b;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2)函数返回值

#include <iostream>
using namespace std;
int func() {
	return 0;
}

int main() {
	int a = 1;
	int b = 2;
	const int& ref = func()
	int& ref = func() //报错 : 不可以
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
7.6.2 可以指不同类型的数据
#include <iostream>
using namespace std;

int func() {
	return 0;
}
int main() {
	int a = 1;
	const double& ref = a;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
7.6.3 常引用作为函数参数时

可以接受const和非const实参(非const引用,只能接受非const实参)

#include <iostream>
using namespace std;

//在函数必须使用引用的参数时
int sum(const int& v1, const int& v2) {
	return v1 + v2;
}
int main() {
	int a = 1;
	int b = 1;
	sum(a, b);
	sum(10, 20);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以跟非const引用构成重载

int sum(const int& v1, const int& v2) {
	return v1 + v2;
}
int sum( int& v1,  int& v2) {
	return v1 + v2;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可以跟非const引用构成重载(此规则也适用于const指针

void sum(const int* v1, const int* v2) {
}
void sum( int* v1,  int* v2) {
}
  • 1
  • 2
  • 3
  • 4
7.6.4 当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量
#include <iostream>
using namespace std;

int main() {
	int age = 10;
	const int& ref =  age;
	age = 30;
	cout << "age is " << age << endl;
	cout << "ref is " << ref << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

输出结果:
age is 30
ref is 30

#include <iostream>
using namespace std;

int main() {
	int age = 10;
	const long& ref = age;
	age = 30;
	cout << "age is " << age << endl;
	cout << "ref is " << ref << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

输出结果:
age is 30
ref is 10

原创不易,你的支持是作者更新的动力

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/175538
推荐阅读
相关标签
  

闽ICP备14008679号