第8章 字符串 1. 字符串的深入理解 1.1 字符串的基本概念 在 C 语言中,字符串是由零个或多个字符组成的序列,以空字符 '\0' 结尾。空字符是一个值为 0 的字符,用于标记字符串的结束。
1.1.1 字符串的内存表示 字符串在内存中是连续存储的字符序列,最后跟着一个空字符。
1 2 3 +---+---+---+---+---+---+ | H | e | l | l | o | \0| +---+---+---+---+---+---+
对于字符串字面量,编译器会在内存的只读数据段中为其分配空间。
对于字符数组,编译器会在栈或堆中为其分配空间(取决于数组的作用域)。
1.2 字符串的表示 1.2.1 字符数组 字符数组是最常用的字符串表示方式,可以修改其中的字符。
1 2 3 4 5 6 7 8 char str[10 ]; char str1[] = "Hello" ; char str2[10 ] = "World" ; char str3[] = {'H' , 'e' , 'l' , 'l' , 'o' , '\0' }; char str4[] = {'H' , 'e' , 'l' , 'l' , 'o' };
1.2.2 字符串字面量 字符串字面量是用双引号括起来的字符序列,存储在只读内存中,不能修改。
1 2 3 4 5 6 "Hello, World!" "This is a\nmultiline\nstring" "String with\tescape\tcharacters" "Hello, " "World!"
1.2.3 指针和字符串 可以使用指针来指向字符串,这种方式更加灵活。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const char *str1 = "Hello" ;char str2[] = "World" ;char *str3 = str2;char *str4 = (char *)malloc (10 * sizeof (char ));if (str4 != NULL ){ strcpy (str4, "Hello" ); free (str4); }
1.3 字符串的特性 以空字符结尾 - 字符串必须以 '\0' 结尾,否则不是有效的 C 字符串连续存储 - 字符串的字符在内存中连续存储长度可变 - 字符串的长度由空字符的位置决定字符编码 - 字符串中的字符使用 ASCII 或其他编码(如 UTF-8)2. 字符串库函数详解 C 语言标准库 <string.h> 提供了丰富的字符串处理函数。
2.1 字符串长度函数 2.1.1 strlen 函数 1 size_t strlen (const char *s) ;
功能 :计算字符串的长度,不包括空字符参数 :s - 指向字符串的指针返回值 :字符串的长度注意 :如果 s 不是以空字符结尾,会导致未定义行为1 2 char str[] = "Hello" ;size_t len = strlen (str);
2.1.2 strnlen 函数 1 size_t strnlen (const char *s, size_t maxlen) ;
功能 :计算字符串的长度,但最多检查 maxlen 个字符参数 :s - 指向字符串的指针,maxlen - 最大检查长度返回值 :字符串的长度或 maxlen(取较小值)注意 :C99 引入,用于防止对非空终止字符串的无限循环1 2 char str[] = "Hello" ;size_t len = strnlen(str, 10 );
2.2 字符串复制函数 2.2.1 strcpy 函数 1 char *strcpy (char *dest, const char *src) ;
功能 :将 src 指向的字符串复制到 dest 指向的缓冲区参数 :dest - 目标缓冲区,src - 源字符串返回值 :指向 dest 的指针注意 :dest 必须有足够的空间容纳 src 字符串不检查缓冲区大小,可能导致缓冲区溢出 src 必须以空字符结尾1 2 3 char dest[10 ];char src[] = "Hello" ;strcpy (dest, src);
2.2.2 strncpy 函数 1 char *strncpy (char *dest, const char *src, size_t n) ;
功能 :将 src 指向的字符串的前 n 个字符复制到 dest 指向的缓冲区参数 :dest - 目标缓冲区,src - 源字符串,n - 要复制的最大字符数返回值 :指向 dest 的指针注意 :如果 src 的长度小于 n,会用空字符填充到 n 个字符 如果 src 的长度大于或等于 n,不会在 dest 末尾添加空字符 1 2 3 char dest[10 ];char src[] = "Hello" ;strncpy (dest, src, 10 );
2.3 字符串拼接函数 2.3.1 strcat 函数 1 char *strcat (char *dest, const char *src) ;
功能 :将 src 指向的字符串拼接到 dest 指向的字符串末尾参数 :dest - 目标字符串,src - 要拼接的字符串返回值 :指向 dest 的指针注意 :dest 必须有足够的空间容纳拼接后的字符串dest 和 src 都必须以空字符结尾1 2 3 char dest[20 ] = "Hello, " ;char src[] = "World!" ;strcat (dest, src);
2.3.2 strncat 函数 1 char *strncat (char *dest, const char *src, size_t n) ;
功能 :将 src 指向的字符串的前 n 个字符拼接到 dest 指向的字符串末尾参数 :dest - 目标字符串,src - 要拼接的字符串,n - 要拼接的最大字符数返回值 :指向 dest 的指针注意 :总是在拼接后的字符串末尾添加空字符 dest 必须以空字符结尾1 2 3 char dest[20 ] = "Hello, " ;char src[] = "World!" ;strncat (dest, src, 3 );
2.4 字符串比较函数 2.4.1 strcmp 函数 1 int strcmp (const char *s1, const char *s2) ;
功能 :比较两个字符串参数 :s1 - 第一个字符串,s2 - 第二个字符串返回值 :小于 0:s1 小于 s2 等于 0:s1 等于 s2 大于 0:s1 大于 s2 注意 :比较是基于字符的 ASCII 值1 2 3 char str1[] = "apple" ;char str2[] = "banana" ;int result = strcmp (str1, str2);
2.4.2 strncmp 函数 1 int strncmp (const char *s1, const char *s2, size_t n) ;
功能 :比较两个字符串的前 n 个字符参数 :s1 - 第一个字符串,s2 - 第二个字符串,n - 要比较的最大字符数返回值 :与 strcmp 相同1 2 3 char str1[] = "apple" ;char str2[] = "applesauce" ;int result = strncmp (str1, str2, 5 );
2.5 字符串查找函数 2.5.1 strchr 函数 1 char *strchr (const char *s, int c) ;
功能 :在字符串中查找字符 c 第一次出现的位置参数 :s - 要搜索的字符串,c - 要查找的字符返回值 :指向找到的字符的指针,如果未找到则返回 NULL1 2 char str[] = "Hello, World!" ;char *ptr = strchr (str, 'W' );
2.5.2 strrchr 函数 1 char *strrchr (const char *s, int c) ;
功能 :在字符串中查找字符 c 最后一次出现的位置参数 :s - 要搜索的字符串,c - 要查找的字符返回值 :指向找到的字符的指针,如果未找到则返回 NULL1 2 char str[] = "Hello, World!" ;char *ptr = strrchr (str, 'o' );
2.5.3 strstr 函数 1 char *strstr (const char *haystack, const char *needle) ;
功能 :在字符串 haystack 中查找子字符串 needle 第一次出现的位置参数 :haystack - 要搜索的字符串,needle - 要查找的子字符串返回值 :指向找到的子字符串的指针,如果未找到则返回 NULL1 2 char str[] = "Hello, World!" ;char *ptr = strstr (str, "World" );
2.6 字符串分割函数 2.6.1 strtok 函数 1 char *strtok (char *str, const char *delimiters) ;
功能 :分割字符串为标记参数 :str - 要分割的字符串(第一次调用)或 NULL(后续调用),delimiters - 分隔符字符串返回值 :指向下一个标记的指针,如果没有更多标记则返回 NULL注意 :第一次调用时,str 指向要分割的字符串 后续调用时,str 应为 NULL 会修改原始字符串,将分隔符替换为 NULL 1 2 3 4 5 6 7 char str[] = "apple,banana,orange" ;char *token = strtok(str, "," );while (token != NULL ){ printf ("%s\n" , token); token = strtok(NULL , "," ); }
2.7 内存操作函数 2.7.1 memset 函数 1 void *memset (void *s, int c, size_t n) ;
功能 :将内存块的前 n 个字节设置为值 c参数 :s - 指向内存块的指针,c - 要设置的值,n - 要设置的字节数返回值 :指向 s 的指针1 2 char str[10 ];memset (str, '0' , 10 );
2.7.2 memcpy 函数 1 void *memcpy (void *dest, const void *src, size_t n) ;
功能 :将 src 指向的内存块的前 n 个字节复制到 dest 指向的内存块参数 :dest - 目标内存块,src - 源内存块,n - 要复制的字节数返回值 :指向 dest 的指针注意 :dest 和 src 不能重叠1 2 3 char src[] = "Hello" ;char dest[10 ];memcpy (dest, src, 6 );
2.7.3 memmove 函数 1 void *memmove (void *dest, const void *src, size_t n) ;
功能 :与 memcpy 类似,但可以处理 dest 和 src 重叠的情况参数 :与 memcpy 相同返回值 :指向 dest 的指针1 2 char str[] = "Hello, World!" ;memmove(str + 7 , str + 0 , 5 );
2.7.4 memcmp 函数 1 int memcmp (const void *s1, const void *s2, size_t n) ;
功能 :比较两个内存块的前 n 个字节参数 :s1 - 第一个内存块,s2 - 第二个内存块,n - 要比较的字节数返回值 :小于 0:s1 小于 s2 等于 0:s1 等于 s2 大于 0:s1 大于 s2 1 2 3 char str1[] = "Hello" ;char str2[] = "Hello" ;int result = memcmp (str1, str2, 5 );
2.8 安全的字符串函数 C11 标准引入了一些安全的字符串函数,它们在 <string.h> 中声明。
2.8.1 strcpy_s 函数 1 errno_t strcpy_s (char *dest, rsize_t destsz, const char *src) ;
功能 :安全地复制字符串参数 :dest - 目标缓冲区,destsz - 目标缓冲区的大小,src - 源字符串返回值 :成功返回 0,失败返回错误码注意 :如果 destsz 不够大,会调用约束处理函数1 2 char dest[10 ];strcpy_s(dest, sizeof (dest), "Hello" );
2.8.2 strcat_s 函数 1 errno_t strcat_s (char *dest, rsize_t destsz, const char *src) ;
功能 :安全地拼接字符串参数 :dest - 目标字符串,destsz - 目标缓冲区的大小,src - 要拼接的字符串返回值 :成功返回 0,失败返回错误码1 2 char dest[20 ] = "Hello, " ;strcat_s(dest, sizeof (dest), "World!" );
2.8.3 strnlen_s 函数 1 size_t strnlen_s (const char *s, rsize_t maxlen) ;
功能 :安全地计算字符串长度参数 :s - 要计算长度的字符串,maxlen - 最大检查长度返回值 :字符串的长度或 maxlen(取较小值)1 2 char str[] = "Hello" ;size_t len = strnlen_s(str, 10 );
2.8.4 strtok_s 函数 1 char *strtok_s (char *str, const char *delimiters, char **context) ;
功能 :安全地分割字符串参数 :str - 要分割的字符串(第一次调用)或 NULL(后续调用),delimiters - 分隔符字符串,context - 用于存储分割状态的指针返回值 :指向下一个标记的指针,如果没有更多标记则返回 NULL1 2 3 4 5 6 7 8 char str[] = "apple,banana,orange" ;char *context;char *token = strtok_s(str, "," , &context);while (token != NULL ){ printf ("%s\n" , token); token = strtok_s(NULL , "," , &context); }
3. 字符串操作的高级技巧 3.1 字符串反转 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <stdio.h> #include <string.h> void reverse_string (char *str) { if (str == NULL ) { return ; } int length = strlen (str); char *start = str; char *end = str + length - 1 ; while (start < end) { char temp = *start; *start = *end; *end = temp; start++; end--; } } int main (void ) { char str[] = "Hello, World!" ; printf ("原字符串:%s\n" , str); reverse_string(str); printf ("反转后:%s\n" , str); return 0 ; }
3.2 字符串大小写转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <stdio.h> #include <ctype.h> void to_upper (char *str) { if (str == NULL ) { return ; } while (*str != '\0' ) { *str = toupper ((unsigned char )*str); str++; } } void to_lower (char *str) { if (str == NULL ) { return ; } while (*str != '\0' ) { *str = tolower ((unsigned char )*str); str++; } } int main (void ) { char str1[] = "Hello, World!" ; char str2[] = "HELLO, WORLD!" ; to_upper(str1); printf ("转换为大写:%s\n" , str1); to_lower(str2); printf ("转换为小写:%s\n" , str2); return 0 ; }
3.3 字符串去空格 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <stdio.h> #include <ctype.h> char *trim_whitespace (char *str) { if (str == NULL ) { return NULL ; } while (isspace ((unsigned char )*str)) { str++; } if (*str == '\0' ) { return str; } char *end = str + strlen (str) - 1 ; while (end > str && isspace ((unsigned char )*end)) { end--; } *(end + 1 ) = '\0' ; return str; } int main (void ) { char str[] = " Hello, World! " ; printf ("原字符串:'%s'\n" , str); printf ("去空格后:'%s'\n" , trim_whitespace(str)); return 0 ; }
3.4 字符串替换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #include <stdio.h> #include <string.h> #include <stdlib.h> char *replace_string (const char *src, const char *old_str, const char *new_str) { if (src == NULL || old_str == NULL || new_str == NULL ) { return NULL ; } size_t src_len = strlen (src); size_t old_len = strlen (old_str); size_t new_len = strlen (new_str); if (old_len == 0 ) { return strdup(src); } int count = 0 ; const char *p = src; while ((p = strstr (p, old_str)) != NULL ) { count++; p += old_len; } size_t new_str_len = src_len + count * (new_len - old_len); char *result = (char *)malloc (new_str_len + 1 ); if (result == NULL ) { return NULL ; } char *q = result; p = src; while ((p = strstr (p, old_str)) != NULL ) { size_t len = p - src; memcpy (q, src, len); q += len; memcpy (q, new_str, new_len); q += new_len; src = p + old_len; p = src; } strcpy (q, src); return result; } int main (void ) { const char *src = "Hello, World! World is beautiful." ; const char *old_str = "World" ; const char *new_str = "C Language" ; char *result = replace_string(src, old_str, new_str); if (result != NULL ) { printf ("原字符串:%s\n" , src); printf ("替换后:%s\n" , result); free (result); } return 0 ; }
4. 字符串的内存管理 4.1 字符串的内存分配 4.1.1 栈上分配 优点 :分配和释放速度快缺点 :空间大小固定,可能不够用4.1.2 堆上分配 1 2 3 4 5 6 char *str = (char *)malloc (100 * sizeof (char )); if (str != NULL ){ free (str); }
优点 :空间大小可以动态调整缺点 :需要手动管理内存,容易导致内存泄漏4.1.3 静态存储区分配 优点 :生命周期长,不需要手动释放缺点 :空间大小固定,可能导致内存浪费4.2 字符串的内存泄漏 字符串操作中常见的内存泄漏情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 char *get_string (void ) { char *str = (char *)malloc (100 * sizeof (char )); if (str != NULL ) { strcpy (str, "Hello" ); } return str; } void use_string (void ) { char *str = get_string(); if (str != NULL ) { printf ("%s\n" , str); free (str); } }
4.3 字符串的缓冲区溢出 字符串操作中常见的缓冲区溢出情况:
1 2 3 4 5 6 7 8 char str[10 ];strcpy (str, "Hello, World!" ); char str[10 ];strncpy (str, "Hello, World!" , sizeof (str) - 1 );str[sizeof (str) - 1 ] = '\0' ;
5. 字符串的编码和国际化 5.1 ASCII 编码 ASCII(American Standard Code for Information Interchange)是最基本的字符编码,使用 7 位表示 128 个字符。
5.2 扩展 ASCII 编码 扩展 ASCII 使用 8 位表示 256 个字符,包含了一些特殊符号和非英语字符。
5.3 Unicode 编码 Unicode 是一种国际标准编码,旨在包含世界上所有的字符。
5.3.1 UTF-8 编码 UTF-8 是一种变长编码,使用 1-4 个字节表示一个字符:
ASCII 字符使用 1 个字节 欧洲和中东字符使用 2 个字节 东亚字符使用 3 个字节 其他字符使用 4 个字节 5.3.2 UTF-16 编码 UTF-16 使用 2 或 4 个字节表示一个字符。
5.3.3 UTF-32 编码 UTF-32 使用 4 个字节表示一个字符。
5.4 多字节字符串 C 语言提供了 <wchar.h> 头文件,用于处理宽字符和多字节字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> #include <wchar.h> #include <locale.h> int main (void ) { setlocale(LC_ALL, "" ); wchar_t wstr[] = L"你好,世界!" ; wprintf(L"%ls\n" , wstr); return 0 ; }
6. 字符串处理的性能优化 6.1 避免不必要的字符串复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 char *process_string (const char *str) { char *copy = strdup(str); free (copy); return strdup(str); } const char *process_string (const char *str) { return str; }
6.2 使用适当的字符串函数 1 2 3 4 5 6 7 8 9 char str[100 ] = "" ;strcat (str, "Hello" );strcat (str, ", " );strcat (str, "World!" );char str[100 ];snprintf (str, sizeof (str), "%s%s%s" , "Hello" , ", " , "World!" );
6.3 预分配足够的内存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 char *build_string (void ) { char *str = NULL ; size_t len = 0 ; for (int i = 0 ; i < 100 ; i++) { char temp[10 ]; sprintf (temp, "%d" , i); size_t temp_len = strlen (temp); char *new_str = (char *)realloc (str, len + temp_len + 1 ); if (new_str == NULL ) { free (str); return NULL ; } str = new_str; strcat (str, temp); len += temp_len; } return str; } char *build_string (void ) { char *str = (char *)malloc (1000 * sizeof (char )); if (str == NULL ) { return NULL ; } str[0 ] = '\0' ; for (int i = 0 ; i < 100 ; i++) { sprintf (str + strlen (str), "%d" , i); } return str; }
6.4 使用指针操作替代数组下标 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int count_vowels (const char *str) { int count = 0 ; for (size_t i = 0 ; i < strlen (str); i++) { char c = str[i]; if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ) { count++; } } return count; } int count_vowels (const char *str) { int count = 0 ; while (*str != '\0' ) { char c = *str; if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ) { count++; } str++; } return count; }
7. 常见的字符串相关错误和调试 7.1 常见错误 7.1.1 缓冲区溢出 1 2 3 char str[10 ];strcpy (str, "Hello, World!" );
7.1.2 空指针解引用 1 2 3 char *str = NULL ;strlen (str);
7.1.3 字符串没有以空字符结尾 1 2 3 char str[5 ] = {'H' , 'e' , 'l' , 'l' , 'o' };strlen (str);
7.1.4 修改字符串字面量 1 2 3 char *str = "Hello" ;str[0 ] = 'h' ;
7.1.5 内存泄漏 1 2 3 4 5 6 7 char *get_string (void ) { char *str = (char *)malloc (100 * sizeof (char )); strcpy (str, "Hello" ); return str; }
7.2 调试技巧 7.2.1 使用断言 1 2 3 4 5 6 7 8 9 10 11 #include <assert.h> char *safe_strcpy (char *dest, size_t dest_size, const char *src) { assert(dest != NULL ); assert(src != NULL ); assert(dest_size > 0 ); }
7.2.2 使用调试宏 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #define DEBUG 1 #if DEBUG #define LOG_STRING(str) printf("%s: %s\n" , #str, str) #else #define LOG_STRING(str) do { } while (0) #endif int main (void ) { char str[] = "Hello" ; LOG_STRING(str); return 0 ; }
7.2.3 使用内存调试工具 Valgrind - 用于检测内存泄漏和内存错误AddressSanitizer - 用于检测内存错误LeakSanitizer - 用于检测内存泄漏8. 字符串的高级应用示例 8.1 示例 1:简单的字符串解析器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <stdio.h> #include <string.h> #include <stdlib.h> void parse_key_value (const char *str) { char *copy = strdup(str); if (copy == NULL ) { return ; } char *token = strtok(copy, "," ); while (token != NULL ) { char *key = token; char *value = strchr (token, '=' ); if (value != NULL ) { *value = '\0' ; value++; printf ("键: %s, 值: %s\n" , key, value); } token = strtok(NULL , "," ); } free (copy); } int main (void ) { const char *str = "name=John,age=30,city=New York" ; parse_key_value(str); return 0 ; }
8.2 示例 2:简单的字符串加密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <stdio.h> void caesar_encrypt (char *str, int shift) { if (str == NULL ) { return ; } while (*str != '\0' ) { if (isalpha ((unsigned char )*str)) { char base = islower ((unsigned char )*str) ? 'a' : 'A' ; *str = (char )(((*str - base + shift) % 26 + 26 ) % 26 + base); } str++; } } void caesar_decrypt (char *str, int shift) { caesar_encrypt(str, 26 - shift); } int main (void ) { char str[] = "Hello, World!" ; int shift = 3 ; printf ("原字符串:%s\n" , str); caesar_encrypt(str, shift); printf ("加密后:%s\n" , str); caesar_decrypt(str, shift); printf ("解密后:%s\n" , str); return 0 ; }
8.3 示例 3:简单的 JSON 解析器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 #include <stdio.h> #include <string.h> #include <stdlib.h> void parse_json (const char *str) { while (isspace ((unsigned char )*str)) { str++; } if (*str != '{' ) { printf ("不是有效的 JSON 对象\n" ); return ; } str++; while (*str != '}' ) { while (isspace ((unsigned char )*str)) { str++; } if (*str != '"' ) { printf ("无效的键\n" ); return ; } str++; const char *key_start = str; while (*str != '"' ) { str++; } size_t key_len = str - key_start; char *key = (char *)malloc (key_len + 1 ); if (key == NULL ) { return ; } strncpy (key, key_start, key_len); key[key_len] = '\0' ; str++; while (isspace ((unsigned char )*str)) { str++; } if (*str != ':' ) { printf ("缺少冒号\n" ); free (key); return ; } str++; while (isspace ((unsigned char )*str)) { str++; } const char *value_start = str; if (*str == '"' ) { str++; while (*str != '"' ) { str++; } } else if (isdigit ((unsigned char )*str) || *str == '-' ) { while (isdigit ((unsigned char )*str) || *str == '.' || *str == 'e' || *str == 'E' || *str == '+' || *str == '-' ) { str++; } } else if (*str == 't' || *str == 'f' || *str == 'n' ) { if (strncmp (str, "true" , 4 ) == 0 ) { str += 4 ; } else if (strncmp (str, "false" , 5 ) == 0 ) { str += 5 ; } else if (strncmp (str, "null" , 4 ) == 0 ) { str += 4 ; } } size_t value_len = str - value_start; char *value = (char *)malloc (value_len + 1 ); if (value == NULL ) { free (key); return ; } strncpy (value, value_start, value_len); value[value_len] = '\0' ; printf ("键: %s, 值: %s\n" , key, value); free (key); free (value); while (isspace ((unsigned char )*str)) { str++; } if (*str == ',' ) { str++; } while (isspace ((unsigned char )*str)) { str++; } } } int main (void ) { const char *json = "{\"name\": \"John\", \"age\": 30, \"city\": \"New York\"}" ; parse_json(json); return 0 ; }
9. 小结 本章深入介绍了 C 语言中字符串的概念、表示方法、操作技巧以及高级应用。字符串是 C 语言中非常重要的数据类型,在实际编程中经常使用。
9.1 关键知识点 字符串的本质 :以空字符 '\0' 结尾的字符序列字符串的表示 :字符数组、字符串字面量、指针字符串库函数 :strlen、strcpy、strcat、strcmp、strchr、strstr 等安全的字符串函数 :strcpy_s、strcat_s、strnlen_s、strtok_s 等字符串的内存管理 :栈分配、堆分配、静态存储区分配字符串的编码 :ASCII、UTF-8、UTF-16、UTF-32字符串的性能优化 :避免不必要的复制、使用适当的函数、预分配内存、使用指针操作常见错误 :缓冲区溢出、空指针解引用、字符串没有以空字符结尾、修改字符串字面量、内存泄漏9.2 学习建议 多写代码 :通过实际编程练习掌握字符串的使用理解原理 :理解字符串的内存表示和库函数的实现原理注意安全 :始终使用安全的字符串函数,避免缓冲区溢出等问题学习调试 :掌握使用调试工具检测和修复字符串相关错误的技巧关注性能 :了解字符串操作的性能特性,编写高效的字符串处理代码字符串处理是 C 语言编程的基础,掌握好字符串处理技巧对于编写高质量的 C 程序至关重要。通过本章的学习,希望读者能够深入理解字符串的概念,掌握字符串库函数的使用,以及编写安全、高效的字符串处理代码。
在后续章节中,我们将学习结构体和其他复合数据类型,以及文件输入/输出等内容。