教程:深入理解 C 和 C++ 中的 char
和 string
¶
第一部分:基础概念¶
1.1 char
:字符的基本单位¶
- 定义:
char
是 C 和 C++ 的基本数据类型,占用 1 字节(8 位),可以表示一个字符或一个整数(范围通常是 -128 到 127 或 0 到 255,取决于实现是否签名)。 - 用途:
- 表示单个字符(如
'A'
、'b'
)。 - 通过数组或指针组成字符串(C 风格字符串)。
- 初始化:
C 1 2 3
char c1 = 'A'; // 单字符,用单引号 char c2 = 65; // 等价于 'A'(ASCII 值) char str[] = "Hello"; // 字符数组,自动包含 '\0'
1.2 string
:C++ 的字符串类¶
- 定义:
string
是 C++ 标准库中的类(std::string
),定义在<string>
头文件中。它封装了对字符串的管理和操作。 - 用途:
- 处理完整的字符串,支持动态长度和丰富操作。
- 初始化:
C++ 1 2 3 4
#include <string> std::string s1 = "Hello"; // 直接初始化 std::string s2(5, 'a'); // 初始化为 "aaaaa" std::string s3 = s1 + " World"; // 拼接初始化
1.3 核心联系¶
char
是string
的构建块:string
内部维护一个动态的char
数组。- 转换:
string
到char*
:用.c_str()
或.data()
。char*
到string
:直接赋值或构造。
第二部分:操作与用法¶
2.1 char
的操作¶
- 单个字符:
-
直接赋值、比较或打印。
C 1 2 3 4 5 6
#include <stdio.h> int main() { char c = 'A'; printf("Character: %c, ASCII: %d\n", c, c); // 输出: Character: A, ASCII: 65 return 0; }
-
C 风格字符串:
- 用
char
数组或指针表示,末尾以\0
结尾。 - 操作依赖
<string.h>
的函数。C 1 2 3 4 5 6 7 8 9 10 11
#include <stdio.h> #include <string.h> int main() { char str1[] = "Hello"; char str2[20]; strcpy(str2, str1); // 复制 strcat(str2, " World"); // 拼接 printf("Result: %s, Length: %lu\n", str2, strlen(str2)); return 0; } // 输出: Result: Hello World, Length: 11
2.2 string
的操作¶
- 基本操作:
-
拼接、访问、长度等。
C++ 1 2 3 4 5 6 7 8 9
#include <iostream> #include <string> int main() { std::string s = "Hello"; s += " World"; // 拼接 std::cout << s[0] << std::endl; // 访问首字符: H std::cout << "Length: " << s.length() << std::endl; // 输出: Length: 11 return 0; }
-
高级操作:
- 查找、替换、截取等。
C++ 1 2 3 4 5 6 7 8 9
std::string s = "Hello World"; size_t pos = s.find("World"); // 查找子串 if (pos != std::string::npos) { std::cout << "Found at: " << pos << std::endl; // 输出: 6 } s.replace(pos, 5, "C++"); // 替换 std::cout << s << std::endl; // 输出: Hello C++ std::string sub = s.substr(0, 5); // 截取 std::cout << sub << std::endl; // 输出: Hello
2.3 对比示例¶
- 拼接:
char
:C 1 2
char str[20] = "Hello"; strcat(str, " World");
-
string
:C++ 1 2
std::string s = "Hello"; s += " World";
-
长度:
char
:strlen(str)
(不含\0
)。string
:s.length()
或s.size()
。
第三部分:内存管理¶
3.1 char
的内存管理¶
- 固定大小:
char
数组的大小在定义时固定。C 1 2
char str[5]; // 最多存 4 个字符 + '\0' strcpy(str, "Hello"); // 溢出!需要 6 字节
- 手动管理:
- 动态分配用
malloc
或new
。C 1 2 3
char* str = (char*)malloc(20); strcpy(str, "Hello"); free(str); // 手动释放
3.2 string
的内存管理¶
- 动态调整:
string
自动分配和释放内存。C++ 1 2
std::string s = "Hello"; s += " World"; // 自动扩展
- 手动控制(可选):
- 用
reserve
预分配空间提高性能。C++ 1 2 3
std::string s; s.reserve(100); // 预留 100 字节 s = "Hello";
3.3 内存对比¶
char
:需要开发者关注边界和释放。string
:自动管理,无需担心溢出或释放。
第四部分:实际应用场景¶
4.1 char
的典型用法¶
- 与 C 库交互:
C 1 2 3
#include <stdio.h> char str[] = "Hello"; printf("%s\n", str);
- 低级操作:
C 1 2 3 4 5
char buffer[256]; int i = 0; while ((buffer[i] = getchar()) != '\n' && i < 255) i++; buffer[i] = '\0'; printf("Input: %s\n", buffer);
4.2 string
的典型用法¶
- 用户输入处理:
C++ 1 2 3 4 5 6 7 8 9
#include <iostream> #include <string> int main() { std::string input; std::cout << "Enter text: "; std::getline(std::cin, input); // 支持多词输入 std::cout << "You entered: " << input << std::endl; return 0; }
- 文件路径操作:
C++ 1 2
std::string path = "/usr/local"; path += "/bin";
4.3 混合使用¶
- 日志格式化:
C++ 1 2 3 4 5 6 7 8 9
#include "spdlog/spdlog.h" int main() { auto logger = spdlog::stdout_color_mt("console"); char c = 'A'; std::string s = "Error code: "; s += c; logger->error(s); return 0; }
第五部分:高级主题¶
5.1 char
的变体¶
signed char
和unsigned char
:signed char
:-128 到 127。-
unsigned char
:0 到 255。C 1 2
unsigned char uc = 255; printf("%d\n", uc); // 输出: 255
-
const char*
: - 表示只读字符串,常用于函数参数。
C 1 2 3
void print(const char* s) { printf("%s\n", s); }
5.2 string
的高级功能¶
- 迭代器:
C++ 1 2 3 4
std::string s = "Hello"; for (auto it = s.begin(); it != s.end(); ++it) { std::cout << *it << " "; } // 输出: H e l l o
- 容量管理:
C++ 1 2 3 4
std::string s = "Hello"; std::cout << "Size: " << s.size() << ", Capacity: " << s.capacity() << std::endl; s.reserve(50); std::cout << "New Capacity: " << s.capacity() << std::endl;
第六部分:注意事项与最佳实践¶
6.1 char
¶
- 缓冲区溢出:
- 始终确保数组足够大。
C 1 2
char str[5]; strcpy(str, "Hello"); // 危险!溢出
- 空字符:
- 手动添加
\0
。C 1
char str[6] = {'H', 'e', 'l', 'l', 'o'}; // 无 '\0',不是合法字符串
6.2 string
¶
- 性能:
- 小字符串操作频繁时,避免过多拷贝。
C++ 1 2 3 4 5
std::string s; for (int i = 0; i < 1000; i++) s += "a"; // 低效 // 优化: s.reserve(1000); for (int i = 0; i < 1000; i++) s += "a";
- 异常安全:
- 检查操作是否成功。
C++ 1 2
std::string s; if (s.empty()) std::cout << "Empty string" << std::endl;
6.3 string 到 char*(C 风格字符串)¶
方法:
- 用 .c_str()
:返回只读的 const char*
。
- 用 .data()
:返回底层字符数组的指针(C++11 前是 const char*,之后可修改但需谨慎)。
- 用 copy
:将内容复制到现有的 char
数组。
示例:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
第七部分:总结¶
char
:- 适合低级控制、C 兼容场景。
- 需要手动管理内存和边界。
string
:- 适合现代 C++ 开发,功能强大且安全。
- 自动管理内存,操作简便。
- 选择依据:
- 用
char
:需要与 C 库交互或极致性能。 - 用
string
:需要便捷性和复杂操作。
int main(int argc, char **argv)
¶
int main(int argc, char **argv)
是程序主函数的一种标准签名,用于接收命令行参数
- argc
:表示命令行参数的数量(argument count),包括程序名本身。
- argv
:表示命令行参数的内容(argument vector),是一个字符串数组。
char** 的含义¶
char**
是指向指针的指针:
- char*
:指向一个字符数组(字符串)。
- char**
:指向一个指针数组,而这些指针又指向字符串。
为什么不用其他类型?¶
-
为什么不用
char*
?char*
只能表示单个字符串,无法表示多个字符串的集合。 示例:char* argv 只能存储 "hello",无法容纳 "hello" 和 "world"。 -
为什么不用 std::string? main 是 C 的遗产,设计时没有 C++ 的 std::string。 保持与 C 兼容性,避免依赖 C++ 标准库。