在 C++ 中,string 是标准库提供的字符串类,指向 string 的指针(string*) 用法和指向普通自定义类型的指针逻辑一致,但需结合 string 的特性理解。下面从核心概念、常见用法、避坑点三方面详细讲解。
一、核心概念:string* 是什么?
string* 是指向 string 对象的指针,存储的是 string 对象在内存中的首地址,而非 string 存储的字符串内容。
类比理解:
string s = "hello";→ 定义了一个string对象s,它在内存中占据一块空间(存储字符串、长度、容量等);string* p = &s;→ 指针p指向s的内存地址(相当于 “记录了s的门牌号”)。
二、string* 的核心用法(结合代码场景)
1. 定义与初始化
string* 必须指向已存在的 string 对象(避免野指针),常见初始化方式:
cpp
运行
#include <iostream>
#include <string>
using namespace std;
int main() {
// 1. 定义string对象,指针指向它(最常用)
string str = "apple";
string* p1 = &str; // &取str的地址,赋值给string*指针
// 2. 指针数组:存储多个string对象的地址(你代码中的核心场景)
string str1 = "banana", str2 = "cherry", str3 = "date";
string* arr[3] = {&str1, &str2, &str3}; // 指针数组,每个元素是string*
// 3. 动态分配string对象(较少用,需手动释放)
string* p2 = new string("grape"); // 堆上创建string对象,p2指向它
return 0;
}
2. 解引用:通过指针访问 string 对象
指针本身只是地址,必须通过解引用(*) 才能访问指向的 string 对象,进而操作字符串内容。
| 操作场景 | 语法示例 | 说明 |
|---|---|---|
| 访问字符串内容 | cout << *p1; | 解引用指针,输出 p1 指向的 string 对象的字符串(输出:apple) |
| 比较字符串内容 | if (*p1 > *p2) | 解引用后用 string 重载的 > 比较字符串内容(而非比较指针地址) |
调用 string 成员函数 | (*p1).size() / p1->size() | 访问 string 的成员(长度、拼接、查找等),-> 是解引用 + 成员访问的简写 |
| 修改字符串内容 | *p1 = "orange"; | 解引用后直接赋值,修改指向的 string 对象的内容 |
示例代码:
cpp
运行
int main() {
string str = "apple";
string* p = &str;
// 1. 解引用访问字符串内容
cout << *p << endl; // 输出:apple
// 2. 调用string成员函数(两种写法等价)
cout << (*p).size() << endl; // 输出:5(解引用后用.访问成员)
cout << p->size() << endl; // 输出:5(推荐!-> 是指针的成员访问符)
// 3. 修改字符串内容
*p = "banana";
cout << *p << endl; // 输出:banana
// 4. 拼接字符串
p->append(" cherry"); // 等价于 (*p).append(" cherry")
cout << *p << endl; // 输出:banana cherry
return 0;
}
3. 指针数组:string* arr[]
string* arr[] 是存储 string* 指针的数组,每个元素指向一个独立的 string 对象,排序时只需交换指针地址(无需拷贝字符串内容,效率高)。
cpp
运行
// 排序函数:指针数组传参,n是元素个数
void sortStrings(string* arr[], int n) {
string* temp; // 临时指针,用于交换地址
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-1-i; j++) {
// 关键:解引用指针,比较字符串内容(而非地址)
if (*arr[j] > *arr[j+1]) {
// 仅交换指针地址,字符串内容不动
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
int main() {
string a = "pear", b = "apple", c = "grape";
string* arr[3] = {&a, &b, &c}; // 指针数组指向3个string对象
sortStrings(arr, 3); // 排序指针数组
// 遍历指针数组,输出结果
for (int i = 0; i < 3; i++) {
cout << *arr[i] << endl; // 解引用arr[i](string*),输出字符串
}
// 输出:apple → grape → pear
return 0;
}
4. 动态数组:string* 指向 string 数组
若需要动态创建多个 string 对象,可让 string* 指向 string 数组的首地址:
cpp
运行
int main() {
// 静态string数组,指针指向数组首元素
string strs[] = {"cat", "dog", "bird"};
string* p = strs; // 数组名strs等价于首元素地址&strs[0]
// 遍历数组(指针方式)
for (int i = 0; i < 3; i++) {
cout << *p << " "; // 输出:cat dog bird
p++; // 指针后移,指向下一个string对象
}
// 动态string数组(堆上分配)
string* pArr = new string[3]{"red", "green", "blue"};
for (int i = 0; i < 3; i++) {
cout << pArr[i] << " "; // 等价于*(pArr+i),输出:red green blue
}
delete[] pArr; // 动态数组需手动释放
return 0;
}
三、避坑点(新手最容易错)
1. 不要直接把 string 对象赋值给 string*
错误写法(你最初的问题):
cpp
运行
string str = "apple";
string* p = str; // ❌ 错误!str是string对象,不是地址,类型不匹配
正确写法:
cpp
运行
string* p = &str; // ✅ 取str的地址,赋值给string*
2. 避免野指针(指针指向已销毁的 string 对象)
cpp
运行
string* p;
{
string temp = "hello";
p = &temp; // temp是局部变量,出括号后销毁
}
cout << *p; // ❌ 未定义行为!p指向已释放的内存(野指针)
3. 指针比较 vs 字符串内容比较
string* 直接用 >/< 比较的是内存地址,而非字符串内容,必须解引用后比较:
cpp
运行
string s1 = "banana", s2 = "apple";
string* p1 = &s1, *p2 = &s2;
if (p1 > p2) {
cout << "指针地址p1 > p2"; // 可能成立(地址随机),但无业务意义
}
if (*p1 > *p2) {
cout << "字符串s1 > s2"; // 成立("banana" > "apple"),符合业务逻辑
}
4. 范围 for 遍历指针数组的注意事项
for(string* p : arr) 中,arr 必须是固定长度的数组(编译器能推导长度),若数组长度通过参数传入,需改用普通 for 循环:
cpp
运行
// 通用写法(适配任意长度n)
void printStrings(string* arr[], int n) {
for (int i = 0; i < n; i++) {
cout << *arr[i] << endl;
}
}
四、总结:string* 核心口诀
- 定义:
string* p = &str;(指针指向string对象的地址); - 访问:
*p取对象,p->成员调函数(如p->size()); - 比较:解引用后比内容(
*p1 > *p2),不要直接比指针; - 数组:
string* arr[]存多个string地址,排序交换地址更高效; - 避坑:不赋野指针、不直接赋值
string对象给string*。
3376

被折叠的 条评论
为什么被折叠?



