news 2026/4/18 5:35:40

指向字符串的指针变量

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
指向字符串的指针变量

一、引言:字符串在C语言中的独特地位

在C语言中,字符串有着特殊的地位。它不像其他语言那样有专门的字符串类型,而是以字符数组的形式存在,并且以空字符’\0’作为结束标志。这种设计使得字符串既简单又灵活,但也带来了一些复杂性。

想象一下,字符串就像一列火车:

· 每个字符是车厢
· 字符串结束符’\0’是车尾标志
· 指针就是列车时刻表,告诉我们火车在哪里

charstr[]="Hello";// 这列火车有5节载客车厢和1节车尾标志车厢// 内存布局: 'H' 'e' 'l' 'l' 'o' '\0'

二、C语言字符串的本质

2.1 字符串的三种表示方法

C语言中表示字符串有三种主要方式:

#include<stdio.h>voidstring_representations(){printf("=== 字符串的三种表示方法 ===\n\n");// 方法1:字符数组(栈上分配)charstr1[]="Hello World";// 自动添加'\0'printf("方法1 - 字符数组:\n");printf(" 声明: char str1[] = \"Hello World\";\n");printf(" 内容: %s\n",str1);printf(" 大小: sizeof(str1) = %zu 字节\n",sizeof(str1));printf(" 地址: %p\n",(void*)str1);// 方法2:字符指针(指向字符串字面量)char*str2="Hello World";// 指向常量区printf("\n方法2 - 字符指针:\n");printf(" 声明: char *str2 = \"Hello World\";\n");printf(" 内容: %s\n",str2);printf(" 大小: sizeof(str2) = %zu 字节(指针大小)\n",sizeof(str2));printf(" 地址: %p\n",(void*)str2);// 方法3:动态分配的字符串(堆上分配)char*str3=(char*)malloc(12*sizeof(char));// 为"Hello World"分配空间if(str3!=NULL){strcpy(str3,"Hello World");printf("\n方法3 - 动态分配:\n");printf(" 声明: char *str3 = malloc(12); strcpy(str3, \"Hello World\");\n");printf(" 内容: %s\n",str3);printf(" 大小: 动态分配12字节\n");printf(" 地址: %p\n",(void*)str3);free(str3);}printf("\n内存区域对比:\n");printf(" str1: 栈内存(可修改)\n");printf(" str2: 常量区(通常不可修改)\n");printf(" str3: 堆内存(可修改,需手动释放)\n");}

2.2 字符串结束符’\0’的重要性

‘\0’(ASCII值为0)是C语言字符串的"终结者",没有它,字符串操作函数就不知道在哪里停止:

#include<stdio.h>voidnull_terminator_demo(){printf("=== 字符串结束符的重要性 ===\n\n");// 正确的字符串(自动包含'\0')charcorrect[]="Hello";printf("正确字符串: \"%s\"\n",correct);printf("长度: %zu (使用strlen)\n",strlen(correct));printf("内存内容: ");for(inti=0;i<=strlen(correct);i++){// 注意:包含'\0'if(correct[i]=='\0'){printf("'\\0' ");}else{printf("'%c' ",correct[i]);}}printf("\n\n");// 错误的字符串(忘记'\0')charwrong[5]={'H','e','l','l','o'};// 没有'\0'!printf("错误字符串: 没有'\\0'的字符数组\n");printf("打印结果: %s (可能包含垃圾数据)\n",wrong);// 演示strlen在没有'\0'时的行为printf("\n演示字符串函数的行为:\n");chartest1[6]={'H','e','l','l','o','\0'};chartest2[5]={'H','e','l','l','o'};// 没有'\0'printf("test1 (有'\\0'): \"%s\", strlen = %zu\n",test1,strlen(test1));printf("test2 (无'\\0'): ");// 危险!strlen会一直读取直到遇到'\0'size_tlen=0;while(test2[len]!='\0'&&len<100){// 防止无限循环len++;}printf("strlen可能返回 %zu 或导致问题\n",len);printf("\n经验法则: 总是确保字符串以'\\0'结束!\n");}

三、字符指针与字符串操作

3.1 声明和初始化字符串指针

字符串指针的声明和初始化有多种方式,各有特点:

#include<stdio.h>#include<stdlib.h>#include<string.h>voidstring_pointer_declaration(){printf("=== 字符串指针的声明与初始化 ===\n\n");// 1. 指向字符串字面量(常量区)char*ptr1="Immutable String";printf("1. 指向字面量:\n");printf(" ptr1 = \"%s\"\n",ptr1);printf(" 注意: 不能修改内容!\n");// ptr1[0] = 'X'; // 错误:运行时错误(试图修改常量区)// 2. 指向字符数组(栈内存)chararr[]="Mutable String";char*ptr2=arr;// 指向栈上的数组printf("\n2. 指向字符数组:\n");printf(" 原始: %s\n",ptr2);ptr2[0]='X';// 可以修改printf(" 修改后: %s\n",ptr2);// 3. 动态分配(堆内存)char*ptr3=(char*)malloc(20*sizeof(char));if(ptr3){strcpy(ptr3,"Dynamic String");printf("\n3. 动态分配:\n");printf(" 初始: %s\n",ptr3);ptr3[0]='D';// 可以修改printf(" 修改后: %s\n",ptr3);free(ptr3);}// 4. 指向字符串常量数组constchar*ptr4="This is constant";printf("\n4. 使用const保护:\n");printf(" ptr4 = \"%s\"\n",ptr4);printf(" 明确表示不可修改,更安全\n");// ptr4[0] = 'X'; // 编译时错误,而不是运行时错误printf("\n总结:\n");printf(" - 字面量: 常量区,不可修改\n");printf(" - 字符数组: 栈内存,可修改\n");printf(" - 动态分配: 堆内存,可修改,需释放\n");printf(" - const修饰: 明确不可修改,更安全\n");}

3.2 字符串指针的运算

字符串指针支持指针运算,这使得遍历字符串变得非常方便:

#include<stdio.h>voidstring_pointer_arithmetic(){printf("=== 字符串指针运算 ===\n\n");charstr[]="C Programming";char*ptr=str;// 指向字符串开头printf("原始字符串: \"%s\"\n",str);printf("指针初始位置: ptr指向 '%c'\n",*ptr);printf("\n1. 指针自增遍历字符串:\n");printf(" 遍历结果: ");while(*ptr!='\0'){printf("%c",*ptr);ptr++;// 移动到下一个字符}printf("\n 现在ptr指向: '\\0'\n");// 重新指向开头ptr=str;printf("\n2. 指针运算访问特定字符:\n");printf(" ptr + 0 = '%c' (str[0])\n",*(ptr+0));printf(" ptr + 2 = '%c' (str[2])\n",*(ptr+2));printf(" ptr + 5 = '%c' (str[5])\n",*(ptr+5));printf("\n3. 计算字符串长度(手动实现):\n");char*start=str;char*end=str;// 找到字符串结尾while(*end!='\0'){end++;}printf(" 字符串长度: %ld (指针差: end - start)\n",end-start);printf("\n4. 反向遍历字符串:\n");printf(" 反向输出: ");char*reverse_ptr=end-1;// 指向最后一个字符(不是'\0')while(reverse_ptr>=start){printf("%c",*reverse_ptr);reverse_ptr--;}printf("\n");printf("\n5. 指针比较:\n");char*p1=str;char*p2=str+5;printf(" p1 = %p, 指向 '%c'\n",(void*)p1,*p1);printf(" p2 = %p, 指向 '%c'\n",(void*)p2,*p2);if(p1<p2){printf(" p1 在 p2 前面\n");}if(p2>p1){printf(" p2 在 p1 后面\n");}}

四、字符串处理函数详解

4.1 标准库字符串函数

C标准库提供了一系列字符串处理函数,它们都定义在<string.h>中:

#include<stdio.h>#include<string.h>#include<stdlib.h>voidstandard_string_functions(){printf("=== 标准库字符串函数 ===\n\n");// 1. strlen - 字符串长度(不包括'\0')char*str1="Hello World";printf("1. strlen - 字符串长度:\n");printf(" \"%s\" 的长度是 %zu\n",str1,strlen(str1));// 2. strcpy - 字符串复制chardest1[20];strcpy(dest1,"Copy Me");printf("\n2. strcpy - 字符串复制:\n");printf(" 复制后: %s\n",dest1);// 3. strncpy - 安全字符串复制(指定最大长度)chardest2[10];strncpy(dest2,"This is too long",sizeof(dest2)-1);dest2[sizeof(dest2)-1]='\0';// 确保以'\0'结束printf("\n3. strncpy - 安全复制:\n");printf(" 安全复制: %s\n",dest2);// 4. strcat - 字符串连接chardest3[30]="Hello";strcat(dest3," World");printf("\n4. strcat - 字符串连接:\n");printf(" 连接后: %s\n",dest3);// 5. strcmp - 字符串比较char*str2="apple";char*str3="banana";intresult=strcmp(str2,str3);printf("\n5. strcmp - 字符串比较:\n");printf(" \"%s\" 与 \"%s\" 比较: ",str2,str3);if(result<0){printf("小于\n");}elseif(result>0){printf("大于\n");}else{printf("等于\n");}// 6. strchr - 查找字符char*str4="Find the letter e";char*found=strchr(str4,'e');printf("\n6. strchr - 查找字符:\n");if(found){printf(" 在 \"%s\" 中找到 'e',位置: %s\n",str4,found);}else{printf(" 未找到\n");}// 7. strstr - 查找子串char*str5="C Programming Language";char*substr=strstr(str5,"Program");printf("\n7. strstr - 查找子串:\n");if(substr){printf(" 在 \"%s\" 中找到 \"Program\",位置: %s\n",str5,substr);}else{printf(" 未找到\n");}// 8. strtok - 字符串分割charstr6[]="apple,banana,cherry,date";printf("\n8. strtok - 字符串分割:\n");printf(" 分割 \"%s\":\n",str6);char*token=strtok(str6,",");while(token!=NULL){printf(" - %s\n",token);token=strtok(NULL,",");}}

4.2 手动实现字符串函数

理解标准库函数的最好方法是手动实现它们:

#include<stdio.h>#include<assert.h>// 1. 手动实现strlensize_tmy_strlen(constchar*str){constchar*p=str;while(*p!='\0'){p++;}returnp-str;// 指针相减得到字符数}// 2. 手动实现strcpychar*my_strcpy(char*dest,constchar*src){char*d=dest;constchar*s=src;while((*d++=*s++)!='\0'){// 空循环,复制在条件中进行}returndest;}// 3. 手动实现strcatchar*my_strcat(char*dest,constchar*src){char*d=dest;// 找到dest的结尾while(*d!='\0'){d++;}// 追加srcmy_strcpy(d,src);returndest;}// 4. 手动实现strcmpintmy_strcmp(constchar*str1,constchar*str2){while(*str1&&(*str1==*str2)){str1++;str2++;}return*(unsignedchar*)str1-*(unsignedchar*)str2;}// 5. 手动实现strchrchar*my_strchr(constchar*str,intch){while(*str!='\0'){if(*str==ch){return(char*)str;// 找到,返回指针}str++;}if(ch=='\0'){// 特殊处理查找'\0'return(char*)str;}returnNULL;// 未找到}voidimplement_string_functions(){printf("=== 手动实现字符串函数 ===\n\n");// 测试my_strlenprintf("1. my_strlen 测试:\n");char*test_str="Hello";printf(" \"%s\" 长度: %zu\n",test_str,my_strlen(test_str));// 测试my_strcpyprintf("\n2. my_strcpy 测试:\n");chardest1[20];my_strcpy(dest1,"Copied String");printf(" 复制后: %s\n",dest1);// 测试my_strcatprintf("\n3. my_strcat 测试:\n");chardest2[30]="Hello";my_strcat(dest2," World");printf(" 连接后: %s\n",dest2);// 测试my_strcmpprintf("\n4. my_strcmp 测试:\n");printf(" \"apple\" vs \"banana\": %d\n",my_strcmp("apple","banana"));printf(" \"apple\" vs \"apple\": %d\n",my_strcmp("apple","apple"));printf(" \"banana\" vs \"apple\": %d\n",my_strcmp("banana","apple"));// 测试my_strchrprintf("\n5. my_strchr 测试:\n");char*str="Find the letter e";char*found=my_strchr(str,'e');if(found){printf(" 在 \"%s\" 中找到 'e',从位置: %s\n",str,found);}// 验证与标准库的一致性printf("\n6. 验证与标准库的一致性:\n");chartest1[]="Test String";chartest2[]="Another String";assert(my_strlen(test1)==strlen(test1));charcopy1[20],copy2[20];my_strcpy(copy1,test1);strcpy(copy2,test1);assert(strcmp(copy1,copy2)==0);printf(" 所有测试通过!\n");}

五、字符串数组与指针数组

5.1 字符串数组的两种实现方式

处理多个字符串时,有两种主要方式:

#include<stdio.h>#include<string.h>voidstring_arrays(){printf("=== 字符串数组的两种方式 ===\n\n");// 方式1:二维字符数组(矩形数组)printf("方式1: 二维字符数组\n");charnames1[4][20]={// 4个字符串,每个最多19字符+'\0'"Alice","Bob","Charlie","David"};printf(" 内存布局: 连续存储,每行固定20字节\n");for(inti=0;i<4;i++){printf(" names1[%d] = \"%s\" (地址: %p)\n",i,names1[i],(void*)names1[i]);}printf(" 总内存: %zu 字节\n",sizeof(names1));// 可以修改内容names1[0][0]='A';// 正确printf(" 修改后: %s\n\n",names1[0]);// 方式2:字符指针数组printf("方式2: 字符指针数组\n");char*names2[]={// 4个指针,指向字符串字面量"Alice","Bob","Charlie","David"};printf(" 内存布局: 指针数组+字符串字面量\n");for(inti=0;i<4;i++){printf(" names2[%d] = \"%s\" (指针地址: %p, 字符串地址: %p)\n",i,names2[i],(void*)&names2[i],(void*)names2[i]);}printf(" 指针数组大小: %zu 字节\n",sizeof(names2));// 注意:不能修改字符串字面量的内容// names2[0][0] = 'A'; // 错误:运行时错误printf(" 注意: 字符串字面量通常不可修改\n\n");// 方式3:动态分配的指针数组printf("方式3: 动态分配的字符串数组\n");char**names3=malloc(4*sizeof(char*));// 分配指针数组if(names3){for(inti=0;i<4;i++){names3[i]=malloc(20*sizeof(char));// 为每个字符串分配空间sprintf(names3[i],"Dynamic %d",i+1);// 创建字符串}for(inti=0;i<4;i++){printf(" names3[%d] = \"%s\"\n",i,names3[i]);}// 可以修改names3[0][0]='X';printf(" 修改后: %s\n",names3[0]);// 释放内存for(inti=0;i<4;i++){free(names3[i]);}free(names3);}printf("\n对比总结:\n");printf(" 二维数组: 内存连续,固定大小,可修改\n");printf(" 指针数组: 内存不连续,字符串长度可变,字面量不可修改\n");printf(" 动态分配: 最灵活,但需要管理内存\n");}

5.2 命令行参数处理

main函数的参数就是字符串指针数组的典型应用:

#include<stdio.h>// argc: 参数个数// argv: 参数向量(字符串指针数组)intmain(intargc,char*argv[]){printf("=== 命令行参数处理 ===\n\n");printf("程序名: %s\n",argv[0]);printf("参数个数: %d\n",argc-1);if(argc>1){printf("参数列表:\n");for(inti=1;i<argc;i++){printf(" argv[%d] = \"%s\"\n",i,argv[i]);}// 解析参数printf("\n参数解析示例:\n");for(inti=1;i<argc;i++){if(argv[i][0]=='-'){// 选项printf(" 选项: %s\n",argv[i]);// 进一步解析选项for(intj=1;argv[i][j]!='\0';j++){printf(" 子选项: -%c\n",argv[i][j]);}}else{// 参数printf(" 参数: %s\n",argv[i]);}}}else{printf("没有额外参数\n");printf("用法: %s [选项] [参数]\n",argv[0]);}return0;}/* 编译后运行示例: $ ./program -abc input.txt output.txt 输出: === 命令行参数处理 === 程序名: ./program 参数个数: 3 参数列表: argv[1] = "-abc" argv[2] = "input.txt" argv[3] = "output.txt" 参数解析示例: 选项: -abc 子选项: -a 子选项: -b 子选项: -c 参数: input.txt 参数: output.txt */

六、字符串与内存管理

6.1 常见的内存错误

字符串操作是内存错误的常见来源:

#include<stdio.h>#include<stdlib.h>#include<string.h>voidcommon_string_errors(){printf("=== 字符串常见内存错误 ===\n\n");printf("错误1: 缓冲区溢出\n");charbuffer[10];// strcpy(buffer, "This string is too long"); // 溢出!// 正确做法:strncpy(buffer,"This string is too long",sizeof(buffer)-1);buffer[sizeof(buffer)-1]='\0';printf(" 安全复制: %s\n",buffer);printf("\n错误2: 忘记分配内存\n");char*ptr;// strcpy(ptr, "Hello"); // 错误:ptr未初始化// 正确做法:ptr=malloc(10*sizeof(char));if(ptr){strcpy(ptr,"Hello");printf(" 正确分配: %s\n",ptr);free(ptr);}printf("\n错误3: 修改字符串字面量\n");char*literal="Immutable";// literal[0] = 'X'; // 运行时错误// 正确做法: 使用字符数组或constcharmutable[]="Mutable";mutable[0]='X';// 正确printf(" 可修改字符串: %s\n",mutable);printf("\n错误4: 内存泄漏\n");char*leaky=malloc(100*sizeof(char));strcpy(leaky,"This will leak");// 忘记 free(leaky); // 内存泄漏!// 正确做法:free(leaky);leaky=NULL;// 避免悬空指针printf(" 已正确释放内存\n");printf("\n错误5: 使用已释放的内存\n");char*freed=malloc(20*sizeof(char));strcpy(freed,"Some text");free(freed);// printf("%s\n", freed); // 错误:使用已释放的内存freed=NULL;// 安全做法printf(" 已设置指针为NULL\n");printf("\n安全编程建议:\n");printf(" 1. 始终检查边界\n");printf(" 2. 使用strncpy而不是strcpy\n");printf(" 3. 记得释放动态内存\n");printf(" 4. 使用const保护不可修改的字符串\n");printf(" 5. 释放后立即设置指针为NULL\n");}

6.2 安全的字符串处理函数

C11标准引入了安全版本的字符串函数:

#define__STDC_WANT_LIB_EXT1__1// 启用安全函数#include<stdio.h>#include<string.h>voidsafe_string_functions(){printf("=== 安全的字符串函数(C11) ===\n\n");chardest[10];charsrc[]="This is a very long string that might overflow";printf("传统方法(危险):\n");// strcpy(dest, src); // 缓冲区溢出!printf("安全方法1: strncpy\n");strncpy(dest,src,sizeof(dest)-1);dest[sizeof(dest)-1]='\0';printf(" 结果: %s\n",dest);#ifdef__STDC_LIB_EXT1__printf("\n安全方法2: strcpy_s(C11安全函数)\n");// strcpy_s(dest, sizeof(dest), src); // 运行时检查// printf(" 如果溢出会返回错误\n");#elseprintf("\n注意: 此编译器不支持C11安全函数\n");#endifprintf("\n自己实现的安全函数:\n");// 安全的字符串复制char*safe_strcpy(char*dest,size_tdest_size,constchar*src){if(dest==NULL||src==NULL||dest_size==0){returnNULL;}size_ti;for(i=0;i<dest_size-1&&src[i]!='\0';i++){dest[i]=src[i];}dest[i]='\0';returndest;}charbuffer[10];safe_strcpy(buffer,sizeof(buffer),"Hello World");printf(" 安全复制到大小为10的缓冲区: %s\n",buffer);// 安全的字符串连接char*safe_strcat(char*dest,size_tdest_size,constchar*src){if(dest==NULL||src==NULL||dest_size==0){returnNULL;}// 找到dest的结尾size_tdest_len=0;while(dest_len<dest_size&&dest[dest_len]!='\0'){dest_len++;}// 计算剩余空间size_tremaining=dest_size-dest_len;// 追加src(不超过剩余空间-1留给'\0')size_ti;for(i=0;i<remaining-1&&src[i]!='\0';i++){dest[dest_len+i]=src[i];}dest[dest_len+i]='\0';returndest;}charbuf[15]="Hello";safe_strcat(buf,sizeof(buf)," World!");printf(" 安全连接: %s\n",buf);}

七、字符串与文件操作

7.1 文件中的字符串处理

#include<stdio.h>#include<stdlib.h>#include<string.h>voidstring_file_operations(){printf("=== 字符串与文件操作 ===\n\n");// 1. 写入字符串到文件printf("1. 写入字符串到文件:\n");FILE*file=fopen("test.txt","w");if(file){char*lines[]={"Hello World\n","This is a test\n","String file operations\n"};for(inti=0;i<3;i++){fputs(lines[i],file);// 写入字符串}fclose(file);printf(" 已写入3行到test.txt\n");}// 2. 从文件读取字符串printf("\n2. 从文件读取字符串:\n");file=fopen("test.txt","r");if(file){charbuffer[100];intline_num=1;printf(" 文件内容:\n");while(fgets(buffer,sizeof(buffer),file)!=NULL){printf(" 行 %d: %s",line_num++,buffer);// 处理每行字符串// 去除换行符size_tlen=strlen(buffer);if(len>0&&buffer[len-1]=='\n'){buffer[len-1]='\0';}printf(" 处理后的长度: %zu\n",strlen(buffer));}fclose(file);}// 3. 格式化字符串写入printf("\n3. 格式化字符串写入:\n");file=fopen("data.txt","w");if(file){charname[]="Alice";intage=25;floatscore=95.5;fprintf(file,"Name: %s\n",name);fprintf(file,"Age: %d\n",age);fprintf(file,"Score: %.2f\n",score);fclose(file);printf(" 已格式化写入到data.txt\n");}// 4. 读取CSV文件(逗号分隔值)printf("\n4. 读取CSV文件示例:\n");file=fopen("data.csv","w+");if(file){// 写入CSV数据fprintf(file,"Name,Age,Score\n");fprintf(file,"Alice,25,95.5\n");fprintf(file,"Bob,30,88.0\n");fprintf(file,"Charlie,22,92.5\n");// 回到文件开头rewind(file);// 读取并解析CSVprintf(" 解析CSV数据:\n");charline[100];fgets(line,sizeof(line),file);// 跳过标题行while(fgets(line,sizeof(line),file)!=NULL){// 去除换行符line[strcspn(line,"\n")]='\0';// 使用strtok分割char*name=strtok(line,",");char*age_str=strtok(NULL,",");char*score_str=strtok(NULL,",");if(name&&age_str&&score_str){intage=atoi(age_str);floatscore=atof(score_str);printf(" 姓名: %-10s 年龄: %3d 分数: %5.1f\n",name,age,score);}}fclose(file);}// 清理remove("test.txt");remove("data.txt");remove("data.csv");}

7.2 配置文件解析

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<ctype.h>#defineMAX_LINE_LENGTH256#defineMAX_KEY_LENGTH50#defineMAX_VALUE_LENGTH100// 配置文件条目结构typedefstruct{charkey[MAX_KEY_LENGTH];charvalue[MAX_VALUE_LENGTH];}ConfigEntry;// 去除字符串两端的空白字符voidtrim_string(char*str){char*end;// 去除前导空白while(isspace((unsignedchar)*str)){str++;}// 如果是空字符串if(*str=='\0'){return;}// 去除尾部空白end=str+strlen(str)-1;while(end>str&&isspace((unsignedchar)*end)){end--;}// 写入新的结束符*(end+1)='\0';}// 解析配置文件intparse_config_file(constchar*filename,ConfigEntry*configs,intmax_configs){FILE*file=fopen(filename,"r");if(!file){printf("无法打开文件: %s\n",filename);return0;}charline[MAX_LINE_LENGTH];intcount=0;while(fgets(line,sizeof(line),file)!=NULL&&count<max_configs){// 去除换行符line[strcspn(line,"\n")]='\0';// 跳过空行和注释trim_string(line);if(line[0]=='\0'||line[0]=='#'){continue;}// 查找等号char*equal_sign=strchr(line,'=');if(!equal_sign){continue;// 无效行}// 分割键和值*equal_sign='\0';char*key=line;char*value=equal_sign+1;// 去除空白trim_string(key);trim_string(value);if(key[0]!='\0'){// 存储到配置数组strncpy(configs[count].key,key,MAX_KEY_LENGTH-1);configs[count].key[MAX_KEY_LENGTH-1]='\0';strncpy(configs[count].value,value,MAX_VALUE_LENGTH-1);configs[count].value[MAX_VALUE_LENGTH-1]='\0';count++;}}fclose(file);returncount;}// 查找配置值constchar*get_config_value(constchar*key,ConfigEntry*configs,intcount){for(inti=0;i<count;i++){if(strcmp(configs[i].key,key)==0){returnconfigs[i].value;}}returnNULL;}voidconfig_file_demo(){printf("=== 配置文件解析示例 ===\n\n");// 创建示例配置文件FILE*config_file=fopen("app.conf","w");if(config_file){fprintf(config_file,"# 应用程序配置\n\n");fprintf(config_file,"app_name = My Application\n");fprintf(config_file,"version = 1.0.0\n");fprintf(config_file,"max_users = 100\n");fprintf(config_file,"debug_mode = true\n");fprintf(config_file,"log_level = info\n");fclose(config_file);}// 解析配置文件ConfigEntry configs[10];intcount=parse_config_file("app.conf",configs,10);if(count>0){printf("成功解析 %d 个配置项:\n\n",count);for(inti=0;i<count;i++){printf(" %-15s = %s\n",configs[i].key,configs[i].value);}// 查找特定配置printf("\n查找配置项:\n");constchar*keys[]={"app_name","max_users","not_found"};for(inti=0;i<3;i++){constchar*value=get_config_value(keys[i],configs,count);if(value){printf(" %s: %s\n",keys[i],value);}else{printf(" %s: (未找到)\n",keys[i]);}}// 类型转换示例printf("\n类型转换:\n");constchar*max_users_str=get_config_value("max_users",configs,count);if(max_users_str){intmax_users=atoi(max_users_str);printf(" max_users (整数): %d\n",max_users);}constchar*debug_mode_str=get_config_value("debug_mode",configs,count);if(debug_mode_str){// 简单布尔值解析intdebug_mode=0;if(strcmp(debug_mode_str,"true")==0||strcmp(debug_mode_str,"1")==0||strcmp(debug_mode_str,"yes")==0){debug_mode=1;}printf(" debug_mode (布尔): %s\n",debug_mode?"是":"否");}}// 清理remove("app.conf");}

八、字符串加密与编码

8.1 简单的字符串加密算法

#include<stdio.h>#include<string.h>#include<ctype.h>// 凯撒密码:将字符串中的每个字母移动n位voidcaesar_cipher(char*str,intshift){while(*str){if(isalpha(*str)){charbase=islower(*str)?'a':'A';*str=((*str-base+shift)%26)+base;}str++;}}// XOR加密:使用密钥对字符串进行XOR运算voidxor_cipher(char*str,charkey){while(*str){*str=*str^key;// XOR运算str++;}}// 简单的Base64编码(简化版)voidsimple_base64_encode(constchar*input,char*output){constcharbase64_table[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";inti=0,j=0;unsignedcharchar_array_3[3];unsignedcharchar_array_4[4];while(*input){char_array_3[i++]=*(input++);if(i==3){char_array_4[0]=(char_array_3[0]&0xfc)>>2;char_array_4[1]=((char_array_3[0]&0x03)<<4)+((char_array_3[1]&0xf0)>>4);char_array_4[2]=((char_array_3[1]&0x0f)<<2)+((char_array_3[2]&0xc0)>>6);char_array_4[3]=char_array_3[2]&0x3f;for(i=0;i<4;i++){output[j++]=base64_table[char_array_4[i]];}i=0;}}if(i){for(intk=i;k<3;k++){char_array_3[k]='\0';}char_array_4[0]=(char_array_3[0]&0xfc)>>2;char_array_4[1]=((char_array_3[0]&0x03)<<4)+((char_array_3[1]&0xf0)>>4);char_array_4[2]=((char_array_3[1]&0x0f)<<2)+((char_array_3[2]&0xc0)>>6);char_array_4[3]=char_array_3[2]&0x3f;for(intk=0;k<i+1;k++){output[j++]=base64_table[char_array_4[k]];}while(i++<3){output[j++]='=';}}output[j]='\0';}voidstring_encryption_demo(){printf("=== 字符串加密与编码 ===\n\n");// 凯撒密码示例printf("1. 凯撒密码:\n");chartext1[]="Hello World";printf(" 原文: %s\n",text1);caesar_cipher(text1,3);// 移3位printf(" 加密: %s\n",text1);caesar_cipher(text1,-3);// 移回3位printf(" 解密: %s\n\n",text1);// XOR加密示例printf("2. XOR加密:\n");chartext2[]="Secret Message";charkey=0x55;// 加密密钥printf(" 原文: %s\n",text2);xor_cipher(text2,key);printf(" 加密: ");for(inti=0;text2[i]!='\0';i++){printf("%02X ",(unsignedchar)text2[i]);}printf("\n");xor_cipher(text2,key);// 再次XOR恢复原文本printf(" 解密: %s\n\n",text2);// Base64编码示例printf("3. Base64编码:\n");chartext3[]="Hello Base64";charencoded[100];printf(" 原文: %s\n",text3);simple_base64_encode(text3,encoded);printf(" 编码: %s\n\n",encoded);// 实用示例:密码隐藏printf("4. 密码输入隐藏:\n");charpassword[50];inti=0;charch;printf(" 请输入密码: ");// 简单的密码隐藏(不回显)while(1){ch=getchar();if(ch=='\n'||ch=='\r'){break;}elseif(ch==127||ch==8){// 退格键if(i>0){i--;printf("\b \b");// 删除一个星号}}else{if(i<sizeof(password)-1){password[i++]=ch;printf("*");}}}password[i]='\0';printf("\n 您输入的密码长度: %zu\n",strlen(password));// 简单加密存储for(intj=0;j<i;j++){password[j]=password[j]^0xAA;// 简单加密}printf(" 加密后的密码: ");for(intj=0;j<i;j++){printf("%02X ",(unsignedchar)password[j]);}printf("\n");}

九、字符串搜索与替换

9.1 实现文本搜索功能

#include<stdio.h>#include<string.h>#include<stdlib.h>// 查找字符串中所有出现的位置voidfind_all_occurrences(constchar*str,constchar*substr){printf("在 \"%s\" 中查找 \"%s\":\n",str,substr);constchar*pos=str;intcount=0;while((pos=strstr(pos,substr))!=NULL){printf(" 位置 %d: 偏移量 %ld\n",++count,pos-str);pos+=strlen(substr);// 移动到下一个可能的位置}if(count==0){printf(" 未找到\n");}else{printf(" 总共找到 %d 次\n",count);}}// 不区分大小写的字符串比较intcase_insensitive_compare(constchar*str1,constchar*str2){while(*str1&&*str2){if(tolower(*str1)!=tolower(*str2)){returntolower(*str1)-tolower(*str2);}str1++;str2++;}returntolower(*str1)-tolower(*str2);}// 不区分大小写的查找voidfind_case_insensitive(constchar*str,constchar*substr){printf("\n不区分大小写查找 \"%s\":\n",substr);intstr_len=strlen(str);intsub_len=strlen(substr);intcount=0;for(inti=0;i<=str_len-sub_len;i++){// 创建临时子字符串进行比较chartemp[sub_len+1];strncpy(temp,str+i,sub_len);temp[sub_len]='\0';if(case_insensitive_compare(temp,substr)==0){printf(" 位置 %d: 偏移量 %d\n",++count,i);}}if(count==0){printf(" 未找到\n");}}// 字符串替换函数char*replace_string(constchar*str,constchar*old,constchar*new){// 计算新字符串所需的空间intcount=0;constchar*tmp=str;while((tmp=strstr(tmp,old))!=NULL){count++;tmp+=strlen(old);}// 分配内存size_tnew_len=strlen(str)+count*(strlen(new)-strlen(old))+1;char*result=malloc(new_len);if(!result){returnNULL;}char*current=result;constchar*start=str;constchar*found;while((found=strstr(start,old))!=NULL){// 复制旧字符串之前的部分size_tlen=found-start;strncpy(current,start,len);current+=len;// 复制新字符串strcpy(current,new);current+=strlen(new);// 移动到旧字符串之后start=found+strlen(old);}// 复制剩余部分strcpy(current,start);returnresult;}voidsearch_and_replace_demo(){printf("=== 字符串搜索与替换 ===\n\n");chartext[]="The quick brown fox jumps over the lazy dog. The fox is quick.";// 查找所有出现find_all_occurrences(text,"fox");find_all_occurrences(text,"the");// 不区分大小写查找find_case_insensitive(text,"the");// 字符串替换printf("\n字符串替换:\n");printf("原文: %s\n",text);char*replaced=replace_string(text,"fox","cat");if(replaced){printf("替换后: %s\n",replaced);free(replaced);}// 更复杂的替换replaced=replace_string(text,"quick","fast");if(replaced){printf("再次替换: %s\n",replaced);free(replaced);}// 单词统计printf("\n单词统计:\n");char*copy=strdup(text);// 复制字符串char*word;intword_count=0;word=strtok(copy," .,!?");// 按空格和标点分割while(word!=NULL){word_count++;printf(" 单词 %d: %s\n",word_count,word);word=strtok(NULL," .,!?");}printf("总单词数: %d\n",word_count);free(copy);}

十、实战项目:简易文本编辑器

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<ctype.h>#defineMAX_LINES1000#defineMAX_LINE_LENGTH256// 文本缓冲区结构typedefstruct{char**lines;// 字符串指针数组intline_count;// 总行数intcapacity;// 容量}TextBuffer;// 初始化文本缓冲区TextBuffer*create_text_buffer(){TextBuffer*buffer=malloc(sizeof(TextBuffer));if(!buffer)returnNULL;buffer->capacity=100;buffer->line_count=0;buffer->lines=malloc(buffer->capacity*sizeof(char*));if(!buffer->lines){free(buffer);returnNULL;}returnbuffer;}// 添加一行文本intadd_line(TextBuffer*buffer,constchar*text){if(buffer->line_count>=buffer->capacity){// 扩容buffer->capacity*=2;buffer->lines=realloc(buffer->lines,buffer->capacity*sizeof(char*));if(!buffer->lines)return0;}buffer->lines[buffer->line_count]=strdup(text);if(!buffer->lines[buffer->line_count])return0;buffer->line_count++;return1;}// 插入一行文本intinsert_line(TextBuffer*buffer,intposition,constchar*text){if(position<0||position>buffer->line_count){return0;// 位置无效}if(buffer->line_count>=buffer->capacity){buffer->capacity*=2;buffer->lines=realloc(buffer->lines,buffer->capacity*sizeof(char*));if(!buffer->lines)return0;}// 移动后续行for(inti=buffer->line_count;i>position;i--){buffer->lines[i]=buffer->lines[i-1];}buffer->lines[position]=strdup(text);if(!buffer->lines[position])return0;buffer->line_count++;return1;}// 删除一行intdelete_line(TextBuffer*buffer,intposition){if(position<0||position>=buffer->line_count){return0;// 位置无效}free(buffer->lines[position]);// 移动后续行for(inti=position;i<buffer->line_count-1;i++){buffer->lines[i]=buffer->lines[i+1];}buffer->line_count--;return1;}// 保存到文件intsave_to_file(TextBuffer*buffer,constchar*filename){FILE*file=fopen(filename,"w");if(!file)return0;for(inti=0;i<buffer->line_count;i++){fprintf(file,"%s\n",buffer->lines[i]);}fclose(file);return1;}// 从文件加载intload_from_file(TextBuffer*buffer,constchar*filename){FILE*file=fopen(filename,"r");if(!file)return0;charline[MAX_LINE_LENGTH];// 清空当前缓冲区for(inti=0;i<buffer->line_count;i++){free(buffer->lines[i]);}buffer->line_count=0;// 读取文件while(fgets(line,sizeof(line),file)!=NULL){// 去除换行符line[strcspn(line,"\n")]='\0';if(!add_line(buffer,line)){fclose(file);return0;}}fclose(file);return1;}// 查找文本voidsearch_text(TextBuffer*buffer,constchar*pattern){printf("\n查找 \"%s\":\n",pattern);intfound=0;for(inti=0;i<buffer->line_count;i++){char*pos=strstr(buffer->lines[i],pattern);if(pos){printf(" 行 %d: %s\n",i+1,buffer->lines[i]);// 高亮显示匹配部分printf(" ");for(intj=0;j<pos-buffer->lines[i];j++){printf(" ");}for(intj=0;j<strlen(pattern);j++){printf("^");}printf("\n");found=1;}}if(!found){printf(" 未找到\n");}}// 替换文本intreplace_text(TextBuffer*buffer,constchar*old,constchar*new){intreplaced=0;for(inti=0;i<buffer->line_count;i++){char*replaced_line=replace_string(buffer->lines[i],old,new);if(replaced_line&&strcmp(replaced_line,buffer->lines[i])!=0){free(buffer->lines[i]);buffer->lines[i]=replaced_line;replaced=1;}elseif(replaced_line){free(replaced_line);}}returnreplaced;}// 显示文本voiddisplay_text(TextBuffer*buffer){printf("\n当前文本内容:\n");printf("========================================\n");for(inti=0;i<buffer->line_count;i++){printf("%3d: %s\n",i+1,buffer->lines[i]);}printf("========================================\n");}// 释放缓冲区voidfree_text_buffer(TextBuffer*buffer){for(inti=0;i<buffer->line_count;i++){free(buffer->lines[i]);}free(buffer->lines);free(buffer);}voidtext_editor_demo(){printf("=== 简易文本编辑器 ===\n\n");TextBuffer*buffer=create_text_buffer();if(!buffer){printf("创建文本缓冲区失败!\n");return;}// 添加一些示例文本add_line(buffer,"这是第一行文本");add_line(buffer,"这是第二行,包含一些内容");add_line(buffer,"第三行用于演示文本编辑器");add_line(buffer,"第四行包含关键词:文本");intchoice;charinput[MAX_LINE_LENGTH];charfilename[100];intline_num;do{printf("\n菜单:\n");printf("1. 显示文本\n");printf("2. 添加行\n");printf("3. 插入行\n");printf("4. 删除行\n");printf("5. 保存到文件\n");printf("6. 从文件加载\n");printf("7. 查找文本\n");printf("8. 替换文本\n");printf("0. 退出\n");printf("请选择: ");scanf("%d",&choice);getchar();// 消耗换行符switch(choice){case1:display_text(buffer);break;case2:printf("请输入要添加的文本: ");fgets(input,sizeof(input),stdin);input[strcspn(input,"\n")]='\0';// 去除换行符if(add_line(buffer,input)){printf("添加成功!\n");}else{printf("添加失败!\n");}break;case3:printf("请输入行号: ");scanf("%d",&line_num);getchar();// 消耗换行符printf("请输入要插入的文本: ");fgets(input,sizeof(input),stdin);input[strcspn(input,"\n")]='\0';if(insert_line(buffer,line_num-1,input)){printf("插入成功!\n");}else{printf("插入失败!\n");}break;case4:printf("请输入要删除的行号: ");scanf("%d",&line_num);getchar();// 消耗换行符if(delete_line(buffer,line_num-1)){printf("删除成功!\n");}else{printf("删除失败!\n");}break;case5:printf("请输入文件名: ");fgets(filename,sizeof(filename),stdin);filename[strcspn(filename,"\n")]='\0';if(save_to_file(buffer,filename)){printf("保存成功!\n");}else{printf("保存失败!\n");}break;case6:printf("请输入文件名: ");fgets(filename,sizeof(filename),stdin);filename[strcspn(filename,"\n")]='\0';if(load_from_file(buffer,filename)){printf("加载成功!\n");}else{printf("加载失败!\n");}break;case7:printf("请输入要查找的文本: ");fgets(input,sizeof(input),stdin);input[strcspn(input,"\n")]='\0';search_text(buffer,input);break;case8:printf("请输入要替换的文本: ");fgets(input,sizeof(input),stdin);input[strcspn(input,"\n")]='\0';charnew_text[MAX_LINE_LENGTH];printf("请输入替换后的文本: ");fgets(new_text,sizeof(new_text),stdin);new_text[strcspn(new_text,"\n")]='\0';if(replace_text(buffer,input,new_text)){printf("替换成功!\n");}else{printf("未找到匹配文本!\n");}break;case0:printf("退出文本编辑器\n");break;default:printf("无效选择!\n");}}while(choice!=0);free_text_buffer(buffer);}

十一、总结:字符串指针的核心要点

11.1 关键概念回顾

  1. 字符串的本质:以’\0’结尾的字符数组
  2. 字符串指针:指向字符串第一个字符的指针
  3. 内存区域:字符串可以存储在栈、堆或常量区
  4. 安全性:注意缓冲区溢出和内存管理

11.2 常见字符串操作模式

操作 安全做法 危险做法
复制 strncpy, 检查边界 strcpy, 不检查边界
连接 strncat, 计算剩余空间 strcat, 可能溢出
比较 strncmp, 指定长度 strcmp, 可能越界
内存 动态分配,及时释放 静态分配,可能不够

11.3 最佳实践

  1. 始终检查边界:确保目标缓冲区足够大
  2. 使用const修饰不可修改的字符串:提高代码安全性
  3. 记得释放动态内存:防止内存泄漏
  4. 处理字符串结束符:确保字符串正确终止
  5. 考虑本地化:对于多语言支持,注意字符编码
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:13:02

基于Springboot的二手奢侈品购物平台设计与实现【高分通过】

“下周之前把论文绪论和系统分析框架定下来&#xff0c;重点要结合实际需求&#xff0c;让技术落地有依据。” 导师的话还在耳边回响&#xff0c;我盯着电脑屏幕上空白的文档&#xff0c;指尖悬在键盘上迟迟未落。作为计算机专业的学生&#xff0c;开发类论文我写过两篇&#x…

作者头像 李华
网站建设 2026/4/18 3:18:20

React Native文件操作终极指南:RNFetchBlob完整解决方案

React Native文件操作终极指南&#xff1a;RNFetchBlob完整解决方案 【免费下载链接】rn-fetch-blob 项目地址: https://gitcode.com/gh_mirrors/rn/rn-fetch-blob 在移动应用开发中&#xff0c;文件操作是不可或缺的核心功能。无论是处理用户上传的图片、下载离线内容…

作者头像 李华
网站建设 2026/4/18 3:14:10

17、嵌入式网络协议与Linux内核构建全解析

嵌入式网络协议与Linux内核构建全解析 1. 其他应用层协议 HTTP、SMTP和POP3这三种协议在特定的嵌入式问题中十分有用。不过,还有许多其他应用层协议在不同场景下也能发挥重要作用。例如,简单网络管理协议(SNMP)已成为管理网络资源的事实上的标准机制。几乎每个连接到网络…

作者头像 李华
网站建设 2026/4/18 3:16:29

GSE宏编译器在魔兽世界经典版中的终极解决方案指南

GSE宏编译器在魔兽世界经典版中的终极解决方案指南 【免费下载链接】GSE-Advanced-Macro-Compiler GSE is an alternative advanced macro editor and engine for World of Warcraft. It uses Travis for UnitTests, Coveralls to report on test coverage and the Curse packa…

作者头像 李华
网站建设 2026/4/18 3:18:32

2015-2025年城市公共文化数字化采购数据

数据简介 在加快推进公共文化服务数字化、智能化的国家战略背景下&#xff0c;政府采购作为推动文化设施现代化转型的重要政策工具&#xff0c;正逐步从传统的设施建设与运营维护向数字化、网络化、智能化服务采购拓展。公共文化设施的数字化采购不仅提升了文化服务的覆盖范围…

作者头像 李华
网站建设 2026/4/18 3:17:27

64、并发版本系统(CVS)命令详解

并发版本系统(CVS)命令详解 1. 版本差异查看(diff) CVS 支持使用 GNU diff 程序,能全面支持行和组格式选项。以下是一个简单示例,展示了如何使用 cvs diff 查看 Makefile 当前版本与仓库版本之间的差异: $ cvs diff Makefile Index: Makefile =================…

作者头像 李华