本文主要记录嵌入式C语言开发过程中,字符串的操作以及注意事项
1. 字符串的本质
- 在C语言中,字符串本质上是以 null 字符(\0)结尾的字符数组。
- 一个字符串包含了一系列的字符直到遇到第一个 null 字符,这个 null 字符用来标识字符串的结束,它的 ASCII 码值为 0。
例如,一个字符串 “hello” 在内存中实际上是由以下六个连续的字符组成的:‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’。最后的 ‘\0’ 是自动加上的字符串结束符,这也是为什么我们称之为以 null 结尾的字符数组。
这个结尾的 null 字符对于大多数处理字符串的标准函数(如 strlen, strcpy, printf等)是必须的,因为它们依赖于这个 null 字符来确定字符串何时结束。如果没有这个 null 终止符,这些函数就会继续读取内存,直到偶然遇到一个 null 值,这可能导致程序崩溃、数据损坏或其他安全问题。
2. 字符串的定义
// 方式一
char *str1 = {"Hello World"}; // 此处可省略大括号
// 方式二
char str2[] = {"Hello World"}; // 此处可省略大括号
// 方式三
char str3[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
// 方式四
char str4[20] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
-
第一种方式定义
char
类型指针,指向 字符串常量"Hello World"
。 这种方式定义的字符串编译器会自动在结尾加上结束符 \0- 注意:应该使用 const char * 来定义,否则编译器会认为 str1 指向的元素是可以修改的(实际不可以),导致程序中一旦进行了修改编译器不会报错,但是运行会崩溃!
-
第二种方式定义了一个
char
类型的数组str2
,方括号内没有指明大小, 编译器会根据后面给出的字符串大小 自动分配内存空间, 这种定义方式编译器也会自动在字符串的结尾增加结束符 \0。 -
第三种方式将字符串内的每个字符都单独进行了定义
- 注意1:定义空元素时,
‘ ’
引号中间应该有空格,直接引号‘’
,编译器会报错 - 注意2:要手动添加结束符,
'\0'
- 注意1:定义空元素时,
-
第四种方式定义时给出了数组容量,如果数组容量超出了后面定义的元素大小,则编译器会自动将未初始化的元素设置为0。
- 注意1:如果你想通过方式 3 和方式 4 定义字符串数组,请手动为其加上 \0,并且如果你给出了数组长度,也请确认数组长度不小于你定义的元素长度,避免产生数据截断或者数组溢出,确保万无一失!
3. 字符串与字符串组的长度
一般在 C 语言中获取一个元素的长度常用 sizeof() 运算符,而获取字符串长度则常用 strlen() 函数。我们来看一下这两种方式对于我们上文给出的四个字符串的计算结果是什么样的。
// 方式一
char *str1 = {"Hello World"}; // 此处可省略大括号
// 方式二
char str2[] = {"Hello World"}; // 此处可省略大括号
// 方式三
char str3[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
// 方式四
char str4[20] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
int main() {
// sizeof
printf("sizeof(str1) = %d\n", sizeof(str1)); // 8
printf("sizeof(str2) = %d\n", sizeof(str2)); // 12
printf("sizeof(str3) = %d\n", sizeof(str3)); // 12
printf("sizeof(str4) = %d\n", sizeof(str4)); // 20
printf("--------------------\n");
// strlen
printf("strlen(str1) = %d\n", strlen(str1));//11
printf("strlen(str2) = %d\n", strlen(str2));//11
printf("strlen(str3) = %d\n", strlen(str3));//11
printf("strlen(str4) = %d\n", strlen(str4));//11
return 0;
}
-
sizeof
给出的是 元素本身(指针,数组)的大小 -
strlen
则是在运行中进行计算,从给定的地址开始直到遇到第一个结束符之间的元素长度,并且这个 长度不包含结束符,专门用于计算字符串的长度
4. 字符串的常见操作
常用的字符串操作函数(需要包含 string.h 头文件)
● strcpy(dst, src): 复制字符串 src 到 dst。
● strcat(dst, src): 将字符串 src 连接到 dst 的尾部。
● strlen(str): 返回 str 的长度(不包括结尾的空字符)。
● strcmp(str1, str2): 比较两个字符串。如果相等返回0,如果 str1 小于 str2 返回负数,反之返回正数。
● strncpy(dst, src, n): 复制最多 n 个字符从 src 到 dst。
● strncat(dst, src, n): 将最多 n 个字符从 src 连接到 dst 的尾部。
● strncmp(str1, str2, n): 比较两个字符串的前 n 个字符。
字符串与其他类型的转换(需包含 stdlib.h 文件)
● atoi(str): 将字符串转换为整数。
● atof(str): 将字符串转换为浮点数。
● 使用 sprintf 将其他类型转换为字符串。
5. 操作字符串注意事项
字符串的结尾:
C语言中的字符串是以空字符(‘\0’) 结尾的字符数组。这意味着在处理字符串时,总是需要确保字符串的最后一个字符是空字符。
内存分配:
当动态创建字符串时,务必确保为字符串的每个字符以及结尾的空字符分配足够的内存。例如,一个包含10个字符的字符串需要分配11个字符的空间(10个字符加上一个空字符)。
字符串函数的使用:
C标准库中提供了一系列处理字符串的函数,如strcpy(), strcat(), strlen(), strcmp()等。使用这些函数时,需要确保目标数组有足够的空间来存放结果,并且源字符串是以空字符结尾的。
缓冲区溢出:
当使用strcpy()、strcat()等函数时,如果目标缓冲区大小不足以容纳原字符串,会导致缓冲区溢出,这是常见的安全隐患。应该使用更安全的函数,如strncpy()或snprintf(),并且始终检查目标缓冲区的大小g。
字符编码:
根据程序的需求,字符编码(如ASCII、UTF-8等)可能会影响字符串处理。确保在处理多字节字符编码时正确地管理字符串。
字符串的不可变性:
在C语言中,字符串字面量是不可变的。尝试修改字符串字面量会导致未定义行为。应该使用字符数组来存储可修改的字符串。
指针操作:
在处理字符串时,经常会使用指针。正确地理解和使用指针是避免错误和内存泄漏的关键。
字符串和数字之间的转换:
使用atoi(), atof(), sprintf(), sscanf()等函数在字符串和数字之间转换时要小心,确保转换的正确性和安全性。
本文转载于原文章:
今天终于搞明白C语言中的字符串了!