Appearance
字符串
字符串是程序中处理文本数据的基本单位,在C语言中,字符串是由字符组成的数组,以空字符'\0'作为结束标志。在本章节中,我们将学习C语言中字符串的定义、使用和操作方法。
1. 什么是字符串?
在C语言中,字符串是由一系列字符组成的数组,以空字符('\0')作为结束标志。空字符是一个特殊的字符,其ASCII值为0,用于标记字符串的结束。
例如,字符串"Hello"在内存中的表示为:
H e l l o \0注意,虽然我们看到的是5个字符,但在内存中实际占用了6个字节(包括结束标志'\0')。
2. 字符串的定义和初始化
2.1 使用字符数组定义字符串
c
// 方法1:指定数组大小
char str1[6] = { 'H', 'e', 'l', 'l', 'o', '\0' };
// 方法2:不指定数组大小,由编译器自动计算
char str2[] = { 'H', 'e', 'l', 'l', 'o', '\0' };
// 方法3:使用字符串字面量(最常用)
char str3[] = "Hello";2.2 示例
c
#include <stdio.h>
int main() {
// 使用字符数组定义字符串
char str1[6] = { 'H', 'e', 'l', 'l', 'o', '\0' };
char str2[] = { 'W', 'o', 'r', 'l', 'd', '\0' };
char str3[] = "Hello, World!";
// 打印字符串
printf("str1: %s\n", str1); // Hello
printf("str2: %s\n", str2); // World
printf("str3: %s\n", str3); // Hello, World!
return 0;
}2.3 注意事项
- 当使用字符串字面量初始化字符数组时,编译器会自动在末尾添加
'\0' - 当使用字符列表初始化字符数组时,需要手动添加
'\0' - 数组大小应至少为字符串长度加1(用于存放结束标志)
3. 字符串的输入和输出
3.1 使用printf输出字符串
c
printf("%s", str);3.2 使用scanf输入字符串
c
scanf("%s", str);注意:scanf会在遇到空格、制表符或换行符时停止读取,因此无法读取包含空格的字符串。
3.3 使用gets输入字符串(不推荐)
c
gets(str);注意:gets函数不会检查数组边界,可能导致缓冲区溢出,因此不推荐使用。
3.4 使用fgets输入字符串(推荐)
c
fgets(str, size, stdin);str:字符数组size:数组大小stdin:标准输入流
注意:fgets会读取换行符并将其存储在字符串中。
3.5 示例
c
#include <stdio.h>
int main() {
char str1[50];
char str2[50];
printf("请输入一个单词: ");
scanf("%s", str1); // 无法读取包含空格的字符串
printf("你输入的单词: %s\n", str1);
// 清除输入缓冲区的换行符
while (getchar() != '\n');
printf("请输入一个句子: ");
fgets(str2, 50, stdin); // 可以读取包含空格的字符串
printf("你输入的句子: %s\n", str2);
return 0;
}4. 字符串处理函数
C语言标准库提供了丰富的字符串处理函数,这些函数定义在头文件<string.h>中。
4.1 字符串长度函数:strlen
c
#include <string.h>
size_t strlen(const char *str);- 功能:计算字符串的长度(不包括结束标志
'\0') - 参数:
str- 字符串 - 返回值:字符串的长度
4.2 字符串复制函数:strcpy, strncpy
c
#include <string.h>
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);- 功能:将源字符串复制到目标字符串
- 参数:
dest- 目标字符串src- 源字符串n- 复制的最大字符数
- 返回值:目标字符串的地址
注意:strcpy会一直复制直到遇到源字符串的'\0',可能导致缓冲区溢出。strncpy则可以指定复制的最大字符数,更加安全。
4.3 字符串连接函数:strcat, strncat
c
#include <string.h>
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);- 功能:将源字符串连接到目标字符串的末尾
- 参数:
dest- 目标字符串src- 源字符串n- 连接的最大字符数
- 返回值:目标字符串的地址
注意:strcat会一直连接直到遇到源字符串的'\0',可能导致缓冲区溢出。strncat则可以指定连接的最大字符数,更加安全。
4.4 字符串比较函数:strcmp, strncmp
c
#include <string.h>
int strcmp(const char *str1, const char *str2);
int strncmp(const char *str1, const char *str2, size_t n);- 功能:比较两个字符串
- 参数:
str1- 第一个字符串str2- 第二个字符串n- 比较的最大字符数
- 返回值:
- 小于0:
str1小于str2 - 等于0:
str1等于str2 - 大于0:
str1大于str2
- 小于0:
4.5 字符串查找函数:strchr, strrchr, strstr
c
#include <string.h>
char *strchr(const char *str, int c);
char *strrchr(const char *str, int c);
char *strstr(const char *haystack, const char *needle);- 功能:
strchr:在字符串中查找指定字符的第一次出现strrchr:在字符串中查找指定字符的最后一次出现strstr:在字符串中查找指定子字符串的第一次出现
- 参数:
str/haystack- 要查找的字符串c- 要查找的字符needle- 要查找的子字符串
- 返回值:
- 找到:返回指向找到位置的指针
- 未找到:返回NULL
4.6 示例
c
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "Hello";
char str2[] = "World";
char str3[20];
// 字符串长度
printf("str1长度: %zu\n", strlen(str1)); // 5
// 字符串复制
strcpy(str3, str1);
printf("str3: %s\n", str3); // Hello
// 字符串连接
strcat(str3, " ");
strcat(str3, str2);
printf("str3: %s\n", str3); // Hello World
// 字符串比较
int result = strcmp(str1, str2);
if (result < 0) {
printf("str1 < str2\n");
} else if (result > 0) {
printf("str1 > str2\n");
} else {
printf("str1 == str2\n");
}
// 字符串查找
char *pos = strchr(str3, 'W');
if (pos != NULL) {
printf("找到 'W' 在位置: %d\n", pos - str3); // 6
}
pos = strstr(str3, "World");
if (pos != NULL) {
printf("找到 'World' 在位置: %d\n", pos - str3); // 6
}
return 0;
}5. 字符串的遍历和修改
5.1 遍历字符串
c
#include <stdio.h>
int main() {
char str[] = "Hello";
int i = 0;
// 方法1:使用索引
printf("方法1(使用索引): ");
while (str[i] != '\0') {
printf("%c ", str[i]);
i++;
}
printf("\n");
// 方法2:使用指针
printf("方法2(使用指针): ");
char *p = str;
while (*p != '\0') {
printf("%c ", *p);
p++;
}
printf("\n");
return 0;
}5.2 修改字符串
c
#include <stdio.h>
int main() {
char str[] = "Hello";
// 修改单个字符
str[0] = 'h';
printf("修改后: %s\n", str); // hello
// 遍历修改字符串
char *p = str;
while (*p != '\0') {
if (*p >= 'a' && *p <= 'z') {
*p = *p - 'a' + 'A'; // 转换为大写
}
p++;
}
printf("转换为大写: %s\n", str); // HELLO
return 0;
}注意:字符串字面量是不可修改的,尝试修改会导致未定义行为。
c
// 错误:尝试修改字符串字面量
char *str = "Hello";
str[0] = 'h'; // 未定义行为6. 字符串的常见操作
6.1 字符串反转
c
#include <stdio.h>
#include <string.h>
void reverse_string(char *str) {
int length = strlen(str);
int start = 0;
int end = length - 1;
while (start < end) {
// 交换字符
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
int main() {
char str[] = "Hello, World!";
printf("原始字符串: %s\n", str); // Hello, World!
reverse_string(str);
printf("反转后: %s\n", str); // !dlroW ,olleH
return 0;
}6.2 字符串转换为整数
c
#include <stdio.h>
int string_to_int(const char *str) {
int result = 0;
int sign = 1;
int i = 0;
// 处理负号
if (str[0] == '-') {
sign = -1;
i = 1;
}
// 转换数字
while (str[i] != '\0') {
if (str[i] >= '0' && str[i] <= '9') {
result = result * 10 + (str[i] - '0');
i++;
} else {
break; // 遇到非数字字符停止
}
}
return result * sign;
}
int main() {
char str1[] = "12345";
char str2[] = "-6789";
int num1 = string_to_int(str1);
int num2 = string_to_int(str2);
printf("str1转换为整数: %d\n", num1); // 12345
printf("str2转换为整数: %d\n", num2); // -6789
return 0;
}6.3 整数转换为字符串
c
#include <stdio.h>
void int_to_string(int num, char *str) {
int i = 0;
int is_negative = 0;
// 处理0的情况
if (num == 0) {
str[0] = '0';
str[1] = '\0';
return;
}
// 处理负数
if (num < 0) {
is_negative = 1;
num = -num;
}
// 转换数字
while (num > 0) {
str[i] = num % 10 + '0';
num = num / 10;
i++;
}
// 添加负号
if (is_negative) {
str[i] = '-';
i++;
}
// 添加结束标志
str[i] = '\0';
// 反转字符串
int start = 0;
int end = i - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
int main() {
char str[20];
int_to_string(12345, str);
printf("12345转换为字符串: %s\n", str); // 12345
int_to_string(-6789, str);
printf("-6789转换为字符串: %s\n", str); // -6789
int_to_string(0, str);
printf("0转换为字符串: %s\n", str); // 0
return 0;
}7. 字符串和指针
7.1 字符串指针
c
// 字符串指针
char *str = "Hello";这里的str是一个指向字符串字面量的指针,字符串字面量存储在只读内存中,不能修改。
7.2 字符数组和字符串指针的区别
| 特性 | 字符数组 | 字符串指针 |
|---|---|---|
| 内存分配 | 在栈上分配内存 | 指向常量区的字符串字面量 |
| 修改 | 可以修改内容 | 不能修改指向的字符串字面量 |
| 初始化 | 可以使用字符列表或字符串字面量 | 只能使用字符串字面量 |
| 大小 | 可以使用sizeof获取数组大小 | sizeof返回指针大小(通常为4或8字节) |
7.3 示例
c
#include <stdio.h>
#include <string.h>
int main() {
// 字符数组
char arr[] = "Hello";
printf("arr: %s\n", arr); // Hello
printf("sizeof(arr): %zu\n", sizeof(arr)); // 6
// 修改字符数组
arr[0] = 'h';
printf("修改后arr: %s\n", arr); // hello
// 字符串指针
char *ptr = "World";
printf("ptr: %s\n", ptr); // World
printf("sizeof(ptr): %zu\n", sizeof(ptr)); // 4或8
// 注意:不能修改字符串字面量
// ptr[0] = 'w'; // 未定义行为
// 指针可以指向不同的字符串
ptr = "Hello";
printf("ptr现在指向: %s\n", ptr); // Hello
return 0;
}8. 动态内存分配与字符串
8.1 使用malloc分配内存
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// 分配内存
char *str = (char *)malloc(100 * sizeof(char));
if (str == NULL) {
printf("内存分配失败\n");
return 1;
}
// 复制字符串
strcpy(str, "Hello, Dynamic Memory!");
printf("str: %s\n", str); // Hello, Dynamic Memory!
// 重新分配内存
str = (char *)realloc(str, 200 * sizeof(char));
if (str == NULL) {
printf("内存分配失败\n");
return 1;
}
// 连接字符串
strcat(str, " This is more text.");
printf("str: %s\n", str); // Hello, Dynamic Memory! This is more text.
// 释放内存
free(str);
return 0;
}8.2 使用strdup复制字符串
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *original = "Hello, World!";
// 使用strdup复制字符串(自动分配内存)
char *copy = strdup(original);
if (copy == NULL) {
printf("内存分配失败\n");
return 1;
}
printf("original: %s\n", original); // Hello, World!
printf("copy: %s\n", copy); // Hello, World!
// 修改复制的字符串
copy[0] = 'h';
printf("修改后copy: %s\n", copy); // hello, World!
printf("original不变: %s\n", original); // Hello, World!
// 释放内存
free(copy);
return 0;
}9. 字符串的注意事项
- 结束标志:字符串必须以
'\0'结束,否则可能导致未定义行为 - 缓冲区溢出:使用字符串处理函数时,要确保目标缓冲区足够大,避免缓冲区溢出
- 字符串字面量:字符串字面量是不可修改的,尝试修改会导致未定义行为
- 内存管理:使用动态内存分配时,记得释放内存,避免内存泄漏
- 输入处理:使用
fgets代替gets,避免缓冲区溢出 - 字符串比较:使用
strcmp函数比较字符串,而不是使用==运算符 - 数组大小:定义字符数组时,要预留足够的空间存放结束标志
'\0'
10. 示例:综合运用
让我们看一个综合运用字符串的例子:
c
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// 字符串处理函数
void to_upper(char *str) {
while (*str != '\0') {
*str = toupper(*str);
str++;
}
}
void to_lower(char *str) {
while (*str != '\0') {
*str = tolower(*str);
str++;
}
}
int count_vowels(const char *str) {
int count = 0;
while (*str != '\0') {
char c = tolower(*str);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
count++;
}
str++;
}
return count;
}
int main() {
char str[100];
// 输入字符串
printf("请输入一个字符串: ");
fgets(str, 100, stdin);
// 移除末尾的换行符
size_t len = strlen(str);
if (len > 0 && str[len - 1] == '\n') {
str[len - 1] = '\0';
}
// 打印原始字符串
printf("原始字符串: %s\n", str);
// 转换为大写
char upper_str[100];
strcpy(upper_str, str);
to_upper(upper_str);
printf("大写: %s\n", upper_str);
// 转换为小写
char lower_str[100];
strcpy(lower_str, str);
to_lower(lower_str);
printf("小写: %s\n", lower_str);
// 计算长度
printf("长度: %zu\n", strlen(str));
// 计算元音字母数量
int vowels = count_vowels(str);
printf("元音字母数量: %d\n", vowels);
return 0;
}11. 小结
字符串是C语言中处理文本数据的基本单位,掌握字符串的处理技巧对于编写实用的C程序至关重要。在本章节中,我们学习了:
- 字符串的定义:C语言中的字符串是由字符组成的数组,以
'\0'作为结束标志 - 字符串的初始化:可以使用字符列表或字符串字面量初始化字符串
- 字符串的输入输出:使用
printf输出字符串,使用scanf或fgets输入字符串 - 字符串处理函数:C标准库提供了丰富的字符串处理函数,如
strlen、strcpy、strcat、strcmp等 - 字符串的遍历和修改:可以使用索引或指针遍历和修改字符串
- 字符串和指针:字符数组和字符串指针有不同的特性和用法
- 动态内存分配:可以使用
malloc和free管理字符串的内存 - 常见操作:字符串反转、转换等常见操作的实现
通过合理使用字符串处理函数和技巧,可以编写出更加灵活、高效的C程序。在使用字符串时,要注意结束标志、缓冲区溢出、内存管理等问题,确保程序的安全性和可靠性。