第十课 指针与引用
指针和引用是C++的特色,掌握它们能让你更好地理解C++的内存管理,初学略有难度。(注:工程项目中,指针与引用普遍使用;算法竞赛一般较少使用,用数组代替。)
一、指针基础
- 指针是存储变量地址的变量
- 使用 * 定义指针
- 使用 & 获取变量地址
- 指针有类型(指向什么类型的变量)
指针定义和使用
#include <iostream>
using namespace std;
int main() {
int a = 10;
// 定义指针:类型 * 指针变量名
int *p = &a; // &获取a的地址
cout << "a的值:" << a << endl; // 10
cout << "a的地址:" << &a << endl; // 0x...(地址)
cout << "p的值:" << p << endl; // 0x...(a的地址)
cout << "*p的值:" << *p << endl; // 10(解引用)
// 通过指针修改值
*p = 20;
cout << "a的新值:" << a << endl; // 20
return 0;
}
二、各种类型的指针
#include <iostream>
using namespace std;
int main() {
int a = 10;
double b = 3.14;
char c = 'A';
int *p1 = &a;
double *p2 = &b;
char *p3 = &c;
cout << "int指针:" << *p1 << endl; // 10
cout << "double指针:" << *p2 << endl; // 3.14
cout << "char指针:" << *p3 << endl; // A
return 0;
}
三、空指针和野指针
#include <iostream>
using namespace std;
int main() {
// 空指针:不指向任何地址
int *p1 = NULL; // C风格
int *p2 = nullptr; // C++11推荐
// 野指针:未初始化的指针(危险!)
// int *p3; // 错误:野指针,指向未知地址
// 使用前一定要初始化
int a = 10;
int *p4 = &a; // 正确
// 判断指针是否为空
if (p1 != nullptr) {
cout << "指针有效" << endl;
} else {
cout << "指针为空" << endl; // 输出这个
}
return 0;
}
四、引用基础
- 引用是变量的别名
- 使用 & 定义引用
- 引用必须在定义时初始化
- 引用不可重新赋值
引用定义和使用
#include <iostream>
using namespace std;
int main() {
int a = 10;
// 定义引用:类型 & 引用名 = 变量名
int &r = a; // r是a的引用(别名)
cout << "a的值:" << a << endl; // 10
cout << "r的值:" << r << endl; // 10(和a一样)
// 通过引用修改值
r = 20;
cout << "a的新值:" << a << endl; // 20(a也变了)
// 引用和原变量完全等价
cout << "&a:" << &a << endl; // 0x...
cout << "&r:" << &r << endl; // 0x...(同一个地址)
return 0;
}
五、指针和引用的区别
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 20;
// 指针可以为空,引用不能
int *p = nullptr; // 合法
// int &r = nullptr; // 错误
// 指针可以重新赋值,引用不能
int *p1 = &a;
p1 = &b; // 合法,指针指向b了
// int &r1 = a;
// r1 = b; // 这是赋值,不是改变引用对象
// 指针有多级指针,引用没有
int **pp = &p1; // 二级指针
// int &&r2 = a; // C++中&&是右值引用,不是二级引用
// sizeof不同
cout << "指针大小:" << sizeof(p1) << endl; // 8(64位系统)
cout << "引用大小:" << sizeof(r1) << endl; // 4(和int一样)
return 0;
}
六、函数参数传递
#include <iostream>
using namespace std;
// 值传递:复制一份,修改不影响原值
void byValue(int x) {
x = 100; // 只修改副本
}
// 指针传递:传地址,可以修改原值
void byPointer(int *p) {
*p = 100; // 修改原值
}
// 引用传递:传别名,可以修改原值
void byReference(int &r) {
r = 100; // 修改原值
}
int main() {
int a = 10;
// 值传递
byValue(a);
cout << "值传递后a:" << a << endl; // 10(未改变)
// 指针传递
byPointer(&a);
cout << "指针传递后a:" << a << endl; // 100(改变了)
a = 10; // 恢复
// 引用传递
byReference(a);
cout << "引用传递后a:" << a << endl; // 100(改变了)
return 0;
}
七、指针和数组
#include <iostream>
using namespace std;
int main() {
int arr[5] = {1, 2, 3, 4, 5};
// 数组名就是首元素地址
int *p = arr; // 等价于 int *p = &arr[0];
// 方式1:下标访问
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
// 方式2:指针访问
for (int i = 0; i < 5; i++) {
cout << *(p + i) << " ";
}
cout << endl;
// 指针算术运算
int *p1 = &arr[0];
int *p2 = &arr[4];
cout << "p2-p1 = " << (p2 - p1) << endl; // 4(元素个数差)
return 0;
}
八、const指针
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 20;
// 常量指针:不能通过指针修改值,但指针本身可以指向其他变量
const int *p1 = &a;
// *p1 = 30; // 错误:不能通过p1修改a的值
p1 = &b; // 合法:p1可以指向其他变量
// 指针常量:指针本身不能指向其他变量,但可以通过指针修改值
int *const p2 = &a;
*p2 = 30; // 合法:可以通过p2修改a的值
// p2 = &b; // 错误:p2不能指向其他变量
// 常量指针常量:都不能改
const int *const p3 = &a;
// *p3 = 40; // 错误
// p3 = &b; // 错误
return 0;
}
九、this指针
#include <iostream>
using namespace std;
class Person {
private:
string name;
int age;
public:
void setInfo(string name, int age) {
this->name = name; // this指向当前对象
this->age = age;
}
void show() {
cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
}
};
int main() {
Person p;
p.setInfo("Tom", 18);
p.show();
return 0;
}
