指针和引用是C++中两个极其重要的概念,它们是实现动态内存管理、高效数据传递和复杂数据结构的基础。本章将深入探讨指针与引用的原理、用法以及它们之间的关系。
指针是一个变量,其值为另一个变量的内存地址。通过指针,我们可以直接访问和操作内存中的数据。
内存地址的概念
在计算机中,每个变量都存储在内存的某个位置,这个位置有一个唯一的地址。就像每家每户都有一个门牌号一样,内存中的每个字节都有一个地址。
#include <iostream> using namespace std; int main() { int num = 100; cout << "变量num的值: " << num << endl; cout << "变量num的地址: " << &num << endl; return 0; }
声明指针变量
指针变量的声明格式为:
数据类型 *指针变量名;
例如:
int *pInt; // 指向int类型的指针 double *pDouble; // 指向double类型的指针 char *pChar; // 指向char类型的指针 void *pVoid; // 通用指针,可以指向任何类型
获取变量的地址
使用取地址运算符 & 可以获取变量的内存地址:
int num = 100; int *p = # // p存储了num的地址
解引用运算符
使用解引用运算符 * 可以访问指针所指向的值:
#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; }
空指针与野指针
int *p1 = nullptr; // C++11推荐的方式,空指针 int *p2 = NULL; // C风格,等价于0 int *p3 = 0; // 直接赋值为0 // int *p4; // 错误!野指针,未初始化
在C++中,数组名本质上是一个指向数组首元素的常量指针。
#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; }
指针支持以下运算: - p + n:指针向后移动n个元素的位置 - p - n:指针向前移动n个元素的位置 - p++ / p–:指针向后/向前移动一个元素 - p - q:两个指针之间的距离(元素个数)
#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; }
#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; }
#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; }
#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; }
通过指针参数,函数可以修改调用者的变量。
#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; }
#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; }
函数指针是指向函数的指针,可以用来实现回调函数等高级功能。
#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; }
引用是变量的别名,它为已存在的变量提供另一个名字。引用必须在声明时初始化,且不能改变引用的对象。
#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; }
引用参数提供了一种更优雅的“按引用传递”方式。
#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; }
引用 vs 指针参数
| 特性 | 指针 | 引用 |
| —— | —— | —— |
| 语法 | 需要解引用(*) | 直接使用 |
| 空值 | 可以为nullptr | 不能为空 |
| 重新赋值 | 可以指向不同对象 | 始终绑定同一对象 |
| 安全性 | 需要检查空指针 | 更安全 |
#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; }
常量引用用于防止函数修改传入的参数,同时避免拷贝开销。
#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; }
| 特性 | 指针 | 引用 |
| —— | —— | —— |
| 定义 | 存储地址的变量 | 变量的别名 |
| 初始化 | 可以不初始化 | 必须立即初始化 |
| 空值 | 可以为空(nullptr) | 不能为空 |
| 修改绑定 | 可以指向不同对象 | 不能改变绑定的对象 |
| 内存占用 | 占用内存存储地址 | 不占用额外内存 |
| 使用语法 | 需要解引用(*) | 直接使用 |
| 算术运算 | 支持 | 不支持 |
| 多级间接 |