c++自学笔记——字符串与指针
字符串与指针
1. 字符串基础
字符串的定义:字符的序列,通常用来表示文本。
字符串字面量(String Literal)是在程序中直接用双引号括起来的文本序列。字符串字面量的类型是 const char*,即指向字符常量的指针。字符串字面量存储在只读内存区域,不能修改。如果尝试修改字符串字面量,会导致编译错误或运行时错误。
"Hello, World!"//字符串字面量
const char* ptr = "Hello"; // ptr 指向字符串字面量
字符串字面量存储方式:以字符串数组的形式存储,并且以空字符\0结尾。
2. 字符串数组
定义:存储字符的数组,可以表示字符串。必须以\0结尾。大小需要足够存储字符串和\0。
char str[] = "Hello";
这行代码定义了一个字符数组str,它存储了字符串"Hello"。实际上,str的内容是:{‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’}。\0是空字符,ASCII值为0,用来标记字符串的结束。
当你用字符串初始化一个字符数组时,编译器会自动加上\0,并分配足够的内存。也可以手动指定大小。如果字符数组的大小小于字符串的长度(包括\0),编译器会报错。
char str[] = "Hello"; // 编译器分配6个字节(5个字符 + 1个'\0')
char str[6] = "Hello"; // 手动指定大小为6
char str[5] = "Hello"; // 错误!数组大小不足以存储字符串和'\0'
不同初始值命令都可以完成数组的初始化,参考下面的代码,有三种方式:
// 在数组中存放字符串并显示(初始化)
#include <iostream>
using namespace std;
int main() {
// 手动初始化字符数组,包含空字符
char s1[] = {'A', 'B', 'C', '\0'};
// 使用字符串字面量初始化,自动添加空字符
char s2[] = {"ABC"};
// 使用字符串字面量初始化,自动添加空字符
char s3[] = "ABC";
// 输出每个数组中的字符串
cout << "字符串 \"" << s1 << "\" 存放在数组 s1 中。\n";
cout << "字符串 \"" << s2 << "\" 存放在数组 s2 中。\n";
cout << "字符串 \"" << s3 << "\" 存放在数组 s3 中。\n";
return 0;
}
如果想要遍历这个数组,因为最后一个字符是\0,只要记住遍历条件是字符不等于0即可。
// 显示接收的字符串
#include <iostream>
using namespace std;
//--- 显示字符串 s ---//
void put_str(const char s[]) {
// 从第一个元素遍历到最后一个元素并显示
for (int i = 0; s[i] != 0; i++) {
cout << s[i];
}
}
int main() {
char str[36]; // 定义一个字符数组,用于存储用户输入的字符串
// 提示用户输入字符串
put_str("字符串:");
cin >> str;
// 显示用户输入的字符串
put_str(str);
cout << '\n';
return 0;
}
之前讲过,不可以对数组进行赋值,所以这样的修改时会编译出错的。
// 修改字符数组
#include <iostream>
using namespace std;
int main() {
// 定义并初始化字符数组
char s[] = "ABC";
// 输出字符数组的值
cout << "s = \"" << s << "\"\n";
// 尝试将字符数组赋值为新的字符串(错误)
s = "XYZ"; // 错误!不能直接给字符数组赋值新的字符串
// 输出字符数组的值
cout << "s = \"" << s << "\"\n";
return 0;
}
3. 字符串指针
定义:指向字符串首地址的指针。只存储地址,不存储字符数据。指向的字符串常量不能修改。
使用方法:
// 字符数组和字符串指针
#include <iostream>
using namespace std;
int main() {
// 字符数组
char str[] = "ABC"; // 字符数组,存储在可读写内存区域
// 字符串指针
char* ptr = "123"; // 定义一个字符指针,指向字符串常量 "Hello"
// 输出字符数组和字符串指针的值
cout << "str = \"" << str << "\"\n";
cout << "ptr = \"" << ptr << "\"\n";
return 0;
}
ptr 是一个指针,它存储的是字符串常量 “123” 的首地址。
字符串常量存储在只读内存区域,不能修改。想要达成修改的效果,是修改其指向的地址。
// 修改字符串指针
#include <iostream>
using namespace std;
int main() {
// 初始化指针 p 指向字符串字面量 "ABC"
char* p = "ABC";
// 输出指针 p 的值
cout << "p = \"" << p << "\"\n";
// 修改指针 p,使其指向新的字符串字面量 "XYZ"
p = "XYZ"; // OK!指针可以指向不同的字符串字面量
// 再次输出指针 p 的值
cout << "p = \"" << p << "\"\n";
return 0;
}
如图所示:
可以使用字符串指针遍历字符串:
char str[] = "Hello";
char* ptr = str; // ptr 指向 str 的首地址
while (*ptr != '\0') { // 遍历直到遇到结束符
cout << *ptr << endl; // 输出当前字符
ptr++; // 指针移动到下一个字符
}
指针可以结合cstring库比较,复制,拼接字符串。
4. 字符串数组与字符串指针的区别
内存分配:
字符串数组:分配固定大小的内存,存储整个字符串。
字符串指针:只存储地址,指向字符串常量。
char str[6] = "Hello"; // 分配6个字节的内存
char* ptr = "Hello"; // 指向字符串常量的地址
可修改性:
字符串数组:可以修改内容。
字符串指针:指向的字符串常量不能修改。
char str[] = "Hello";
str[0] = 'J'; // 修改为 "Jello"
char* ptr = "Hello";
ptr[0] = 'J'; // 错误!尝试修改只读内存
下面的代码展示了字符串字面量和字符串指针字符串数组在代码运行中实际代表的意义的不同:
#include <iostream>
using namespace std;
int main() {
// 字符串字面量
"Hello, World!"; // 这是一个字符串字面量,存储在只读内存区域
// 指针指向字符串字面量
const char* ptr = "Hello"; // ptr 指向字符串字面量 "Hello"
cout << "Pointer value: " << ptr << endl; // 输出 "Hello"
// 尝试修改字符串字面量(会导致编译错误或运行时错误)
// ptr[0] = 'J'; // 错误!尝试修改只读内存
// 指针可以指向不同的字符串字面量
ptr = "World"; // ptr 现在指向字符串字面量 "World"
cout << "New pointer value: " << ptr << endl; // 输出 "World"
// 字符串字面量和字符数组的区别
char arr[] = "Hello"; // 字符数组,存储在可读写内存区域
arr[0] = 'J'; // 合法,可以修改字符数组的内容
cout << "Modified array: " << arr << endl; // 输出 "Jello"
// 指针和字符数组的地址
cout << "Address of ptr: " << &ptr << endl; // 指针本身的地址
cout << "Value of ptr: " << ptr << endl; // 指针指向的地址
cout << "Address of arr: " << &arr << endl; // 字符数组的地址
cout << "Value of arr: " << arr << endl; // 字符数组的首地址
return 0;
}
5. cstring库
简介:C++标准库中的头文件,提供了用于操作C风格字符串的函数。
常用函数:
strlen:获取字符串长度。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
const char* str = "Hello";
cout << "Length: " << strlen(str) << endl; // 输出 5
return 0;
}
strcpy:复制字符串。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char src[] = "Hello";
char dest[6]; // 足够存储 "Hello" 和 '\0'
strcpy(dest, src);
cout << "Copied string: " << dest << endl; // 输出 "Hello"
return 0;
}
strcat:拼接字符串。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char dest[12] = "Hello "; // 初始字符串
const char* src = "World";
strcat(dest, src);
cout << "Concatenated string: " << dest << endl; // 输出 "Hello World"
return 0;
}
strcmp:比较字符串。
**用法:**strcmp(str1, str2) 如果 str1 小于 str2,返回负数。如果 str1 等于 str2,返回 0。如果 str1 大于 str2,返回正数。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
const char* str1 = "Hello";
const char* str2 = "World";
if (strcmp(str1, str2) == 0) {
//如果 str1 小于 str2,返回负数。如果 str1 等于 str2,返回 0。如果 str1 大于 str2,返回正数。
cout << "Strings are equal" << endl;
} else {
cout << "Strings are not equal" << endl; // 输出 "Strings are not equal"
}
return 0;
}
strncpy:安全的字符串复制,只复制指定数量的字符。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
const char* src = "Hello";
char dest[3]; // 只能存储 3 个字符
strncpy(dest, src, 3);
cout << "Copied string: " << dest << endl; // 输出 "Hel"
return 0;
}
strncat:安全的字符串拼接,只拼接指定数量的字符。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char dest[10] = "Hello ";
const char* src = "World";
strncat(dest, src, 5); // 拼接前 5 个字符
cout << "Concatenated string: " << dest << endl; // 输出 "Hello World"
return 0;
}
strncmp:安全的字符串比较,只比较指定数量的字符。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
const char* str1 = "Hello";
const char* str2 = "Hell";
if (strncmp(str1, str2, 4) == 0) {
cout << "First 4 characters are equal" << endl; // 输出 "First 4 characters are equal"
}
return 0;
}
其实在C++中,字符串本质上是一个字符数组,而指针可以用来指向这个数组的首地址。