跳至内容
张叶安的小站
用户工具
登录
站点工具
搜索
工具
显示页面
过去修订
反向链接
最近更改
媒体管理器
网站地图
登录
>
最近更改
媒体管理器
网站地图
您的足迹:
cplus:第八章指针与引用
本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。
====== 第八章 指针与引用 ====== 指针和引用是C++中两个极其重要的概念,它们是实现动态内存管理、高效数据传递和复杂数据结构的基础。本章将深入探讨指针与引用的原理、用法以及它们之间的关系。 ===== 8.1 指针基础 ===== ==== 8.1.1 什么是指针 ==== 指针是一个变量,其值为另一个变量的内存地址。通过指针,我们可以直接访问和操作内存中的数据。 **内存地址的概念** 在计算机中,每个变量都存储在内存的某个位置,这个位置有一个唯一的地址。就像每家每户都有一个门牌号一样,内存中的每个字节都有一个地址。 <code cpp> #include <iostream> using namespace std; int main() { int num = 100; cout << "变量num的值: " << num << endl; cout << "变量num的地址: " << &num << endl; return 0; } </code> **声明指针变量** 指针变量的声明格式为: <code cpp> 数据类型 *指针变量名; </code> 例如: <code cpp> int *pInt; // 指向int类型的指针 double *pDouble; // 指向double类型的指针 char *pChar; // 指向char类型的指针 void *pVoid; // 通用指针,可以指向任何类型 </code> ==== 8.1.2 指针的初始化与使用 ==== **获取变量的地址** 使用取地址运算符 **&** 可以获取变量的内存地址: <code cpp> int num = 100; int *p = # // p存储了num的地址 </code> **解引用运算符** 使用解引用运算符 ***** 可以访问指针所指向的值: <code cpp> #include <iostream> using namespace std; int main() { int num = 100; int *p = # cout << "num的值: " << num << endl; cout << "p的值(地址): " << p << endl; cout << "*p的值: " << *p << endl; // 通过指针访问num的值 *p = 200; // 通过指针修改num的值 cout << "修改后num的值: " << num << endl; return 0; } </code> **空指针与野指针** <code cpp> int *p1 = nullptr; // C++11推荐的方式,空指针 int *p2 = NULL; // C风格,等价于0 int *p3 = 0; // 直接赋值为0 // int *p4; // 错误!野指针,未初始化 </code> ===== 8.2 指针与数组 ===== ==== 8.2.1 数组名的本质 ==== 在C++中,数组名本质上是一个指向数组首元素的常量指针。 <code cpp> #include <iostream> using namespace std; int main() { int arr[5] = {10, 20, 30, 40, 50}; cout << "arr的值(首地址): " << arr << endl; cout << "&arr[0]: " << &arr[0] << endl; cout << "*arr: " << *arr << endl; // 10 // 通过指针访问数组元素 int *p = arr; for (int i = 0; i < 5; i++) { cout << "*(p+" << i << ") = " << *(p + i) << endl; // 等价于 p[i] 或 arr[i] } return 0; } </code> ==== 8.2.2 指针运算 ==== 指针支持以下运算: - **p + n**:指针向后移动n个元素的位置 - **p - n**:指针向前移动n个元素的位置 - **p++ / p--**:指针向后/向前移动一个元素 - **p - q**:两个指针之间的距离(元素个数) <code cpp> #include <iostream> using namespace std; int main() { int arr[] = {10, 20, 30, 40, 50}; int *p = arr; cout << "初始位置: *p = " << *p << endl; // 10 p++; // 移动到下一个元素 cout << "p++后: *p = " << *p << endl; // 20 p += 2; // 向前移动2个元素 cout << "p+=2后: *p = " << *p << endl; // 40 int *q = arr; cout << "p - q = " << p - q << endl; // 3,相隔3个元素 return 0; } </code> ==== 8.2.3 指针与多维数组 ==== <code cpp> #include <iostream> using namespace std; int main() { int matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; // 使用指针访问二维数组 int (*p)[4] = matrix; // p指向包含4个int的数组 for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { cout << *(*(p + i) + j) << " "; // 等价于 matrix[i][j] } cout << endl; } return 0; } </code> ===== 8.3 指针与字符串 ===== ==== 8.3.1 C风格字符串 ==== <code cpp> #include <iostream> #include <cstring> using namespace std; int main() { // 字符串常量 const char *str1 = "Hello, World!"; // 字符数组 char str2[] = "Hello"; cout << "str1: " << str1 << endl; cout << "str2: " << str2 << endl; cout << "strlen(str1): " << strlen(str1) << endl; // 遍历字符串 const char *p = str1; while (*p != '\0') { cout << *p; p++; } cout << endl; return 0; } </code> ==== 8.3.2 字符串指针数组 ==== <code cpp> #include <iostream> using namespace std; int main() { const char *days[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; for (int i = 0; i < 7; i++) { cout << days[i] << endl; } return 0; } </code> ===== 8.4 指针与函数 ===== ==== 8.4.1 指针作为函数参数 ==== 通过指针参数,函数可以修改调用者的变量。 <code cpp> #include <iostream> using namespace std; // 交换两个数的值(使用指针) void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int main() { int x = 10, y = 20; cout << "交换前: x=" << x << ", y=" << y << endl; swap(&x, &y); cout << "交换后: x=" << x << ", y=" << y << endl; return 0; } </code> ==== 8.4.2 数组作为函数参数 ==== <code cpp> #include <iostream> using namespace std; // 计算数组元素之和 int sum(int *arr, int size) { int total = 0; for (int i = 0; i < size; i++) { total += arr[i]; } return total; } // 或者使用以下形式 int sum2(int arr[], int size) { int total = 0; for (int i = 0; i < size; i++) { total += *(arr + i); } return total; } int main() { int numbers[] = {1, 2, 3, 4, 5}; int size = sizeof(numbers) / sizeof(numbers[0]); cout << "数组元素之和: " << sum(numbers, size) << endl; return 0; } </code> ==== 8.4.3 函数指针 ==== 函数指针是指向函数的指针,可以用来实现回调函数等高级功能。 <code cpp> #include <iostream> using namespace std; // 定义函数类型 int add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } int multiply(int a, int b) { return a * b; } int main() { // 声明函数指针 int (*operation)(int, int); operation = add; cout << "10 + 5 = " << operation(10, 5) << endl; operation = subtract; cout << "10 - 5 = " << operation(10, 5) << endl; operation = multiply; cout << "10 * 5 = " << operation(10, 5) << endl; return 0; } </code> ===== 8.5 引用 ===== ==== 8.5.1 引用的概念 ==== 引用是变量的别名,它为已存在的变量提供另一个名字。引用必须在声明时初始化,且不能改变引用的对象。 <code cpp> #include <iostream> using namespace std; int main() { int num = 100; int &ref = num; // ref是num的引用 cout << "num: " << num << endl; cout << "ref: " << ref << endl; ref = 200; // 通过引用修改值 cout << "修改后num: " << num << endl; cout << "&num: " << &num << endl; cout << "&ref: " << &ref << endl; // 地址相同 return 0; } </code> ==== 8.5.2 引用作为函数参数 ==== 引用参数提供了一种更优雅的"按引用传递"方式。 <code cpp> #include <iostream> using namespace std; // 使用引用交换两个数 void swap(int &a, int &b) { int temp = a; a = b; b = temp; } int main() { int x = 10, y = 20; cout << "交换前: x=" << x << ", y=" << y << endl; swap(x, y); // 不需要取地址符 cout << "交换后: x=" << x << ", y=" << y << endl; return 0; } </code> **引用 vs 指针参数** | 特性 | 指针 | 引用 | |------|------|------| | 语法 | 需要解引用(*) | 直接使用 | | 空值 | 可以为nullptr | 不能为空 | | 重新赋值 | 可以指向不同对象 | 始终绑定同一对象 | | 安全性 | 需要检查空指针 | 更安全 | ==== 8.5.3 引用作为返回值 ==== <code cpp> #include <iostream> using namespace std; int arr[] = {10, 20, 30, 40, 50}; // 返回数组元素的引用 int &getElement(int index) { return arr[index]; } int main() { cout << "修改前: " << arr[2] << endl; getElement(2) = 100; // 通过引用返回值修改数组元素 cout << "修改后: " << arr[2] << endl; return 0; } </code> ==== 8.5.4 常量引用 ==== 常量引用用于防止函数修改传入的参数,同时避免拷贝开销。 <code cpp> #include <iostream> #include <string> using namespace std; // 使用const引用传递大型对象 void printString(const string &str) { cout << str << endl; // str = "new"; // 错误!不能修改const引用 } int main() { string text = "Hello, World!"; printString(text); return 0; } </code> ===== 8.6 指针与引用的比较 ===== | 特性 | 指针 | 引用 | |------|------|------| | 定义 | 存储地址的变量 | 变量的别名 | | 初始化 | 可以不初始化 | 必须立即初始化 | | 空值 | 可以为空(nullptr) | 不能为空 | | 修改绑定 | 可以指向不同对象 | 不能改变绑定的对象 | | 内存占用 | 占用内存存储地址 | 不占用额外内存 | | 使用语法 | 需要解引用(*) | 直接使用 | | 算术运算 | 支持 | 不支持 | | 多级间接 | 支持(如int**) | 不支持 | ===== 8.7 常见指针错误与避免 ===== ==== 8.7.1 常见错误 ==== <code cpp> // 1. 野指针 int *p; // 未初始化 *p = 10; // 危险! // 2. 空指针解引用 int *p2 = nullptr; *p2 = 10; // 运行时错误! // 3. 内存泄漏 void leak() { int *p = new int[100]; // 没有delete,内存泄漏 } // 4. 悬挂指针 int *dangling() { int local = 10; return &local; // 错误!返回局部变量的地址 } // 5. 数组越界 int arr[5]; int *p = arr; *(p + 10) = 100; // 越界访问 </code> ==== 8.7.2 最佳实践 ==== <code cpp> #include <iostream> using namespace std; // 安全的指针使用示例 void safePointerUsage() { // 1. 始终初始化指针 int *p1 = nullptr; // 2. 使用前检查空指针 if (p1 != nullptr) { *p1 = 10; } // 3. 使用智能指针(C++11以后推荐) // unique_ptr<int> smartPtr = make_unique<int>(10); // 4. 及时释放内存并置空 int *p2 = new int(100); delete p2; p2 = nullptr; // 5. 避免返回局部变量的地址 // 如果需要,使用动态分配或返回副本 } int main() { safePointerUsage(); return 0; } </code> ===== 8.8 综合示例 ===== ==== 示例:实现动态数组类 ==== <code cpp> #include <iostream> using namespace std; class DynamicArray { private: int *data; int size; int capacity; public: DynamicArray(int cap = 10) { capacity = cap; size = 0; data = new int[capacity]; } ~DynamicArray() { delete[] data; } void push_back(int value) { if (size >= capacity) { // 扩容 capacity *= 2; int *newData = new int[capacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } delete[] data; data = newData; } data[size++] = value; } int &operator[](int index) { if (index < 0 || index >= size) { cerr << "Index out of bounds!" << endl; exit(1); } return data[index]; } int getSize() const { return size; } void print() const { for (int i = 0; i < size; i++) { cout << data[i] << " "; } cout << endl; } }; int main() { DynamicArray arr; for (int i = 1; i <= 15; i++) { arr.push_back(i * 10); } cout << "数组内容: "; arr.print(); cout << "第5个元素: " << arr[4] << endl; arr[4] = 999; // 通过引用修改 cout << "修改后: "; arr.print(); return 0; } </code> ===== 8.9 习题 ===== ==== 基础题 ==== 1. **指针基础** 声明一个int变量a,值为100。声明一个指向a的指针p,并通过p输出a的值和地址。 2. **指针运算** 编写程序,使用指针遍历一个整型数组,计算所有元素的和。 3. **交换函数** 分别用指针和引用两种方式实现交换两个整数的函数,比较它们的差异。 ==== 进阶题 ==== 4. **字符串反转** 编写一个函数,使用指针实现C风格字符串的反转(不使用额外的数组)。 <code cpp> void reverse(char *str); </code> 5. **数组去重** 编写函数,接收一个已排序的整型数组及其大小,使用指针操作去除重复元素,返回新的数组大小。 6. **函数指针数组** 创建一个计算器程序,使用函数指针数组实现加、减、乘、除四种运算。 ==== 综合题 ==== 7. **矩阵乘法** 使用指针实现两个矩阵的乘法。矩阵用动态分配的二维数组表示。 8. **链表实现** 使用指针实现一个简单的单向链表,包含以下功能: - 在头部插入节点 - 在尾部插入节点 - 删除指定值的节点 - 打印链表 - 释放链表内存 9. **指针与引用的选择** 分析以下场景,说明应该使用指针还是引用,并解释原因: - 函数需要返回多个值 - 函数参数可能是空值 - 实现多态 - 实现运算符重载 ==== 思考题 ==== 10. **深入理解** - 解释为什么引用不能为NULL而指针可以 - 分析sizeof(指针)和sizeof(引用)的结果差异 - 讨论指针和引用在底层实现上的联系与区别 ===== 8.10 小结 ===== 本章深入学习了C++中指针和引用的概念: - **指针**是存储内存地址的变量,通过解引用可以访问和修改目标数据 - **指针运算**允许我们以灵活的方式遍历数组和内存 - **引用**是变量的别名,提供了更安全的间接访问方式 - 引用作为函数参数时,语法更简洁且不易出错 - 指针和引用各有适用场景,需要根据实际情况选择 - 使用指针时要注意避免野指针、空指针解引用、内存泄漏等常见问题 掌握指针和引用是成为C++程序员的重要里程碑,它们是理解更高级概念(如动态内存、STL、多态)的基础。
cplus/第八章指针与引用.txt
· 最后更改:
2026/02/03 22:33
由
127.0.0.1
页面工具
显示页面
过去修订
反向链接
回到顶部