2. 字符串处理库 - <string.h>
2.1. 基础函数
2.1.1. strlen
和 sizeof
区别
strlen
和 sizeof
经常被新手混淆,虽然它们看似都能获取长度,但本质上是不同的。
strlen
:用于获取字符串的实际长度,不包括终止符\0
。该函数从字符串起始位置计数,直到遇到\0
。- 作用:计算字符串中的字符数。
- 用法:
#include <stdio.h> #include <string.h> int main() { char str[] = "Hello"; printf("Length of string: %zu\n", strlen(str)); // 输出:5 return 0; }
sizeof
:返回对象所占的内存字节数,包括数组长度(如果是数组)或数据类型大小。- 作用:获取内存占用,不限定于字符串。
- 用法:
#include <stdio.h> int main() { char str[] = "Hello"; printf("Size of array: %zu\n", sizeof(str)); // 输出:6,因为包括了末尾的 '\0' return 0; }
2.1.2. strcpy
和 strncpy
这两者用于字符串复制,但在处理时有不同的安全考虑。
strcpy
:复制字符串直到遇到\0
,不进行边界检查,这可能导致缓冲区溢出。- 用法:
#include <stdio.h> #include <string.h> int main() { char src[] = "Hello, World!"; char dst[20]; strcpy(dst, src); printf("Destination: %s\n", dst); return 0; }
- 用法:
strncpy
:复制最多指定数量的字符,适用于边界需要控制的场合,尤其在需要防止溢出时。- 用法:
#include <stdio.h> #include <string.h> int main() { char src[] = "Hello, World!"; char dst[10]; strncpy(dst, src, sizeof(dst) - 1); dst[9] = '\0'; // 用 '\0' 结束字符串,因为 strncpy 不保证 null-terminated printf("Destination: %s\n", dst); return 0; }
- 用法:
2.1.3. strcat
和 strncat
strcat
:在目标字符串的结尾追加源字符串,要求目标字符串有足够的空间。- 用法:
#include <stdio.h> #include <string.h> int main() { char str1[20] = "Hello"; char str2[] = ", World!"; strcat(str1, str2); printf("Result: %s\n", str1); return 0; }
- 用法:
strncat
:追加最多指定数量的字符,适用于避免缓冲区溢出。- 用法:
#include <stdio.h> #include <string.h> int main() { char str1[15] = "Hello"; char str2[] = ", World!"; strncat(str1, str2, sizeof(str1) - strlen(str1) - 1); printf("Result: %s\n", str1); return 0; }
- 用法:
2.1.4. strcmp
和 strncmp
strcmp
:逐字符比较两个字符串,如果相等返回0
,否则返回差值。- 用法:
#include <stdio.h> #include <string.h> int main() { char str1[] = "Hello"; char str2[] = "World"; int result = strcmp(str1, str2); printf("Comparison: %d\n", result); // 负值,因为 'H' < 'W' return 0; }
- 用法:
strncmp
:逐字符比较指定数量的字符,增加了长度控制。- 用法:
#include <stdio.h> #include <string.h> int main() { char str1[] = "Hello"; char str2[] = "Hello, World!"; int result = strncmp(str1, str2, 5); // 比较前5个字符 printf("Comparison: %d\n", result); // 0,因为相等 return 0; }
- 用法:
2.2. 高级函数
2.2.1. strstr
的使用
strstr
用于在一个字符串内查找子串的首次出现。
- 用法:
#include <stdio.h> #include <string.h> int main() { char str[] = "Hello, World!"; char *substr = strstr(str, "World"); if (substr) { printf("Found substring: %s\n", substr); } else { printf("Substring not found\n"); } return 0; }
2.2.2. strtok
的解析
strtok
用于分割字符串,不过注意它的破坏性操作。
- 用法:
#include <stdio.h> #include <string.h> int main() { char str[] = "Hello, World! Welcome to C programming."; char *token = strtok(str, " "); while (token != NULL) { printf("Token: %s\n", token); token = strtok(NULL, " "); } return 0; }
2.2.3. strspn
和 strcspn
strspn
和 strcspn
用于测量字符串的子串结构。
strspn
:返回字符串中连续出现在另一个字符串中的字符数量。- 用法:
#include <stdio.h> #include <string.h> int main() { char str[] = "123abc456"; char keys[] = "1234567890"; size_t len = strspn(str, keys); printf("Initial segment length: %zu\n", len); // 输出:3 return 0; }
- 用法:
strcspn
:返回字符串中不在另一个字符串中出现的字符数量。- 用法:
#include <stdio.h> #include <string.h> int main() { char str[] = "123abc456"; char keys[] = "abc"; size_t len = strcspn(str, keys); printf("Initial segment length: %zu\n", len); // 输出:3 return 0; }
- 用法:
2.3. 内存操作
2.3.1. memcpy
和 memmove
memcpy
:拷贝内存区域,源和目标区域不能重叠。- 用法:
#include <stdio.h> #include <string.h> int main() { char src[50] = "Hello, World!"; char dest[50]; memcpy(dest, src, strlen(src) + 1); printf("Destination: %s\n", dest); return 0; }
- 用法:
memmove
:适合重叠区域的内存拷贝。- 用法:
#include <stdio.h> #include <string.h> int main() { char str[] = "memmove can be very useful......"; memmove(str + 20, str + 15, 15); printf("%s\n", str); // 输出:memmove can be very very useful return 0; }
- 用法:
2.3.2. memset
memset
用于初始化内存块。
- 用法:
#include <stdio.h> #include <string.h> int main() { char buffer[50]; memset(buffer, '#', sizeof(buffer)-1); buffer[49] = '\0'; printf("Buffer: %s\n", buffer); return 0; }
2.3.3. memcmp
的比较
memcmp
用于原始内存块的比较。
- 用法:
#include <stdio.h> #include <string.h> int main() { char mem1[] = "abc"; char mem2[] = "abc"; int result = memcmp(mem1, mem2, sizeof(mem1)); if (result == 0) { printf("Memory blocks are equal.\n"); } else { printf("Memory blocks are different.\n"); } return 0; }
详细讲解
1. strlen
vs sizeof
strlen
的应用场景:通常用于获取字符串长度,在字符串操作时确保不会溢出或产生逻辑错误。sizeof
的应用场景:适用于获取数组或数据类型大小,以进行内存管理和缓冲区操作。
2. strcpy
vs strncpy
strcpy
的危险性:如果目标缓冲区不够大,可能写到未分配的内存,因此在重要场合要避免使用。strncpy
的安全性:加以边界的控制,防止缓冲区溢出,但需要手动处理字符串尾的\0
。
3. strcat
vs strncat
- 追加操作:这两个函数用于把一个字符串追加到另一个的末尾。
- 内存安全:
strncat
用于确保对目标字符串的追加不超过容量。
4. strcmp
vs strncmp
- 比较用途:两者用于字符串比较,控制比较字符数能提高效率并防止内存访问错误。
5. strstr
使用
- 在复杂文本处理或者解析场合,
strstr
是一个非常有用的工具来检测子串或分割文本。
6. strtok
的解析
- 注意事项:它会改变源字符串,且多个线程中使用时必须考虑同步问题。
7. strspn
和 strcspn
- 应用场景:通常用于解析配置文件、命令行参数时,查找某些前缀或跳过特定字符集。
8. memcpy
vs memmove
- 对于重叠内存区域,必须使用
memmove
以确保正确的内存复制,否则可能得到不可预期的结果。 - 常用场合:数据结构操作、缓冲区管理、二进制流处理。
9. memset
的用法
- 初始化内存块是一个经常性的任务,如清空缓存、预分配数据结构。
10. memcmp
的比较
- 适用于需要直接比较二进制数据块的场合,常用于协议处理、数据校验。
这些函数是 C 语言中处理字符串和内存的基石,其中容易引入错误的地方主要在于边界检查与内存管理。初学者应谨慎使用特别是涉及到动态内存和字符串处理的函数,关注程序的安全和健壮性。
通过对这些函数的深刻理解和精准使用,你将能编写出高效且安全的 C 程序。在实际开发中,建议基于具体应用环境来选择合适的函数,以保证代码的正确性和健壮性。