在 C 语言中,字符串 和 字符数组 密切相关,但又有重要区别。以下是它们的对比:
1. 基本定义
字符数组 (char array)
- 是一个固定大小的数组,元素类型是
char
。 - 可以存储字符序列,但不一定以
'\0'
结尾。 - 例如:
char arr[5] = {'H', 'e', 'l', 'l', 'o'}; // 不是字符串(无 '\0') char arr2[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 是字符串
字符串 (string)
- 在 C 中,字符串是以
'\0'
(空字符)结尾的字符数组。 - 通常用双引号
" "
定义,编译器会自动添加'\0'
。 - 例如:
char str[] = "Hello"; // 等价于 {'H', 'e', 'l', 'l', 'o', '\0'}
2. 内存分配
特性 | 字符数组 | 字符串(字符数组 + ‘\0’) |
---|---|---|
存储方式 | 栈/堆 | 栈/堆/常量区(字符串字面量) |
可变性 | 可修改 | 如果是栈/堆分配的,可修改;如果是字符串字面量(如 char *s = "abc" ),则不可修改 |
大小 | 固定 | 必须至少比内容多 1 字节(存放 '\0' ) |
3. 初始化方式
字符数组
char arr1[5] = {'a', 'b', 'c', 'd', 'e'}; // 不是字符串
char arr2[6] = {'a', 'b', 'c', 'd', 'e', '\0'}; // 是字符串
字符串
char str1[] = "abc"; // 自动加 '\0',大小为 4
char *str2 = "abc"; // 字符串字面量(存储在常量区,不可修改)
4. 修改内容
- 字符数组(栈/堆分配)可以修改:
char arr[] = "hello"; arr[0] = 'H'; // 合法,修改为 "Hello"
- 字符串字面量(常量区)不可修改:
如果要修改,应该用字符数组或动态分配内存:char *str = "hello"; str[0] = 'H'; // 非法!运行时错误(未定义行为)
char str[] = "hello"; // 栈分配,可修改 char *str = strdup("hello"); // 堆分配,可修改(需 free)
5. 作为函数参数
- 字符数组和字符串都可以作为
char*
传递:void print_str(const char *s) {printf("%s\n", s); }int main() {char arr[] = "World";char *str = "Hello";print_str(arr); // OKprint_str(str); // OKreturn 0; }
6. 常见问题
- 缓冲区溢出:
char arr[5] = "Hello"; // 错误!需要 6 字节(包括 '\0')
- 字符串字面量不可修改:
char *s = "abc"; s[0] = 'x'; // 崩溃!
- 忘记
'\0'
导致非字符串:char arr[3] = {'a', 'b', 'c'}; printf("%s", arr); // 错误!没有 '\0',可能打印乱码
总结
特性 | 字符数组 | 字符串('\0' 结尾的字符数组) |
---|---|---|
存储 | 任意字符序列 | 必须以 '\0' 结尾 |
可变性 | 可修改(栈/堆) | 取决于存储位置(字面量不可修改) |
初始化 | {'a', 'b', 'c'} | "abc" (自动加 '\0' ) |
使用场景 | 需要直接操作内存 | 文本处理、打印、标准库函数(如 strcpy , strlen ) |
推荐做法:
- 如果需要修改字符串,使用字符数组或动态分配内存(
malloc
+strcpy
)。 - 如果只是只读字符串,可以用
const char*
指向字面量。 - 始终确保字符串以
'\0'
结尾,否则strlen
、printf
等函数会出错。