在 C 语言中,字符(Character) 和 字符串(String) 是两个不同但相关的概念。下面详细介绍它们的定义、存储方式和使用方法:
一、字符(Character)
1. 定义与存储
- 基本类型:
char
(1 字节,范围 - 128~127 或 0~255,取决于编译器) - 存储形式:单个字符用单引号
' '
包裹,实际存储为对应的 ASCII 码值
2. 示例代码
#include <stdio.h>
int main() {
// 直接赋值(字符常量)
char c1 = 'A'; // 存储ASCII码值65
char c2 = 66; // 等价于 char c2 = 'B';
char newline = '\n'; // 转义字符:换行符
// 输出字符和对应的ASCII值
printf("c1: %c (ASCII %d)\n", c1, c1); // 输出: A (ASCII 65)
printf("c2: %c (ASCII %d)\n", c2, c2); // 输出: B (ASCII 66)
// 特殊字符(转义序列)
char tab = '\t'; // 制表符
char quote = '\"'; // 双引号字符
return 0;
}
3. 关键注意点
- 转义字符:使用反斜杠
\
表示特殊字符,例如:'\n' // 换行符 '\t' // 制表符 '\0' // 空字符(ASCII 0,字符串终止符) '\\' // 反斜杠本身 '\'' // 单引号 '\"' // 双引号
- 字符与整数的关系:
char
本质是小整数,可参与算术运算:char c = 'A'; printf("%d\n", c + 1); // 输出66('A'的ASCII码是65)
二、字符串(String)
1. 定义与存储
- 本质:由多个字符组成的数组,以空字符
'\0'
结尾 - 两种定义方式:
- 字符数组:
char str[] = "Hello";
- 字符指针:
char *str = "Hello";
- 字符数组:
2. 字符数组 vs 字符指针
字符数组 | 字符指针 |
---|---|
char str[] = "Hello"; | char *str = "Hello"; |
数据存储在栈内存 | 数据存储在只读内存区 |
可修改内容(如 str[0]='J'; ) | 不可修改内容(修改会导致运行时错误) |
数组大小自动计算(含 '\0' ) | 指针指向常量字符串的首地址 |
3. 示例代码
#include <stdio.h>
#include <string.h> // 字符串处理函数库
int main() {
// 1. 字符数组方式(可修改)
char str1[] = "Hello"; // 等价于 char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
printf("str1长度: %zu\n", strlen(str1)); // 输出5(不包含'\0')
printf("str1大小: %zu\n", sizeof(str1)); // 输出6(包含'\0')
// 修改字符数组内容
str1[0] = 'J';
printf("修改后: %s\n", str1); // 输出 "Jello"
// 2. 字符指针方式(指向常量字符串,不可修改)
char *str2 = "World";
// str2[0] = 'X'; // 错误!修改常量字符串会导致运行时错误
// 3. 动态输入字符串(需确保缓冲区足够大)
char input[100];
printf("请输入字符串: ");
scanf("%s", input); // 注意:scanf遇到空格会截断
printf("你输入的是: %s\n", input);
// 4. 字符串处理函数示例
char src[] = "Copy this";
char dest[20];
strcpy(dest, src); // 复制字符串
printf("复制结果: %s\n", dest);
return 0;
}
误区 1:“指针指向的字符串不能修改,所以指针本身不能修改”
- 错误:指针变量本身的值(即所指向的地址)可以修改,例如:
char *str = "Hello"; str = "World"; // 合法!指针重新指向另一个常量字符串
但 不能通过指针修改其所指向的内存内容。
误区 2:“用 char * 定义的字符串都不能修改”
- 错误:如果
char*
指向的是 可写内存(如动态分配的内存或字符数组),则可以修改。例如:char arr[10] = "abc"; char *ptr = arr; // ptr指向栈上的数组(可写) ptr[0] = 'x'; // 合法,修改数组内容为 "xbc"
4. 字符串处理函数
常用的字符串操作函数(需包含 <string.h>
):
函数名 | 功能描述 | 示例 |
---|---|---|
strlen(s) | 返回字符串长度(不含 '\0' ) | strlen("Hello") → 5 |
strcpy(dst, src) | 复制字符串 src 到 dst | strcpy(dest, "Hi"); |
strcat(dst, src) | 将 src 追加到 dst 末尾 | strcat(dest, " World"); |
strcmp(s1, s2) | 比较 s1 和 s2 (按字典序) | strcmp("A", "B") → 负数 |
strchr(s, c) | 在 s 中查找字符 c 第一次出现的位置 | strchr("test", 'e') → 指向 e 的指针 |
三、字符与字符串的区别
特性 | 字符 (char ) | 字符串 (char[] 或 char* ) |
---|---|---|
定义符号 | 单引号 'A' | 双引号 "Hello" |
存储内容 | 单个字符的 ASCII 码值 | 多个字符 + 终止符 '\0' |
占用空间 | 1 字节 | 字符串长度 + 1 字节 |
示例 | char c = 'A'; | char s[] = "ABC"; 或 char *s = "ABC"; |
四、常见错误与注意事项
-
未初始化字符串:
未初始化指针直接使用:
char *ptr; strcpy(ptr, "test"); // 错误!ptr未指向有效内存 // 修正:ptr = (char*)malloc(5); strcpy(ptr, "test");
-
字符串越界:
char str[3]; strcpy(str, "ABCD"); // 错误!超出数组大小,导致缓冲区溢出
-
忘记字符串终止符:
char str[3] = {'A', 'B', 'C'}; // 错误!缺少'\0',不是有效字符串 printf("%s", str); // 可能输出乱码,直到遇到内存中的某个'\0'
-
混淆字符和字符串:
char c = "A"; // 错误!"A"是字符串(含'A'和'\0'),无法赋值给char char c = 'A'; // 正确
补充:预分配内存的常用方法
方法 适用场景 函数 / 语法 示例 栈内存数组 长度固定、编译时已知 char arr[size];
char name[20];
堆内存动态分配 长度动态变化、运行时确定 malloc()
/calloc()
int *ptr = (int*)malloc(n*sizeof(int));
常量字符串 只读场景,无需修改 "Hello"
char *str = "World";
柔性数组 结构体末尾的动态数组 struct { int len; char data[]; }
// C99特性,需动态分配结构体+数组
总结
- 字符:单个字符用
char
类型表示,存储为 ASCII 码值,用单引号包裹。 - 字符串:由字符数组组成,以
'\0'
结尾,用双引号定义。 - 操作字符串:使用标准库函数(如
strlen
、strcpy
)处理字符串,避免手动操作终止符。 - 安全性:确保缓冲区足够大,避免越界和未初始化内存的问题。