本文转载自微信公众号《编码珠》,作者刘亚熙。转载本文请联系Coding诸暨公众号。我们知道c/c++之所以使用起来灵活,很大程度上是因为它可以直接操作内存,所以本文主要讲一下c中的字符串操作函数。1.常量指针和指针常量首先补充一下之前的一篇文章,教大家如何理解c/c++中的指针。我说的是指针的问题。有人说常量指针和指针常量这两个概念总是混淆。我应该怎么办?,例如:inta=100;constint*p=&a;//常量指针,指向的值不能改变,但是指向的地址是可以改变的intconst*p=&a;//等价于上面的公式int*constp=&a;//指针常量,指向的地址不能改变,但是指向的值可以改变那么如何区分常量指针和指针常量,这里有一个技巧,上次忘记告诉大家了article:从左往右看,跳过类型,看修改了哪个字符。如果是*,表示指针指向的值不能改变。如果是指针变量,则表示指针的指向不可改变,指针的值不可修改。你可以通俗地把这个原则理解为“就近原则”。然后回头看第一行代码,就是指针常量:constint*p=&a;我们跳过变量类型int,然后const被*修饰,所以它指向的值不能被修改第二行代码,常量指针:int*constp=&a;同样,我们跳过int,发现const是一个直接修饰的p,所以它的点是不能改变的。两者有细微差别,请注意。本节我们再回到字符串问题。在说字符串复制功能之前,我们先回忆一下C语言中的字符串。我们知道在c语言中定义字符串有两种方式,分别是:charstr1[]="helloworld";//栈区字符串char*str2="helloworld";//数据常量区字符串那么这个有什么区别两个在用?答案是,第一行定义后,操作系统为其分配栈区内存,而如果第二行以指针的形式定义了一个字符串,则其分配的内存区在数据常量区,意思是:itsvaluecannotbechange:str1[0]='m';//正确,字符数组可以修改str2[0]='m';//错误,常量区不能修改,所以,在常量区域,如果我们有两个内容相同但变量不同的指针变量,它们实际上指向同一块内存:char*str1="helloworld";char*str2="helloworld";printf("%p\n",str1);printf("%p\n",str2);在上面两行代码中,我们分别打印出str1和str2指向的内存地址,发现它们的值是一样的。为什么,这是因为常量区内存的值是只读的。即使我们声明了两个不同的变量,只要它们的值相同,那么这两个变量就指向同一个内存区域。这里值得注意的是,在c++中,字符串指针与c语言略有不同。在c++中直接增强了字符串指针,因为c++规定字符串指针必须用const修饰,比如在c++定义中,编译器会直接报错:char*str="helloworld";//报错直接报错constchar*str="helloworld";//更正在实际开发过程中,我们使用字符串,一般使用数组,不推荐使用指针字符串形式,即:charstr[]="helloworld";//推荐使用char*str="helloworld";//不推荐使用So,请注意这方面的细微差别。二、字符串的长度我们知道c语言中的字符串以'\0'结尾,也就是说当你声明一个字符串的时候,系统会自动在你的字符串末尾加上一个'\0''末尾是结束字符,printf每打印一个字符都会检查当前字符是否为'\0',直到遇到'\0'立即停止。这里最令人困惑的是字符串的长度。我们看下面两行代码:先看下面两行代码:charstr1[]="hello";char*str2="hello";printf("%d\n",sizeof(str1));//输出结果为6printf("%d\n",sizeof(str2));//输出结果为4或8,那为什么要用sizeof计算字符串长度呢,两者计算的结果是不同的,而且第一行的长度不是我们想要的,不应该是5吗?这是因为在声明一个字符串的时候,系统会自动在末尾加上一个以'\0'结尾的行作为结束字符,内存模型如下:所以,对于上面两行代码,它们的长度实际上是6。那为什么第二行的输出是4呢?这是因为我们在第二行定义了一个字符串指针,它指向常量区中的一个字符串,而sizeof运算符在对这个指针进行操作的时候,实际上是在计算这个指针的字节长度,而一个指针占用的长度为在x86系统中为4个字节,在x64环境中长度为8个字节。所以,我们在实际计算字符串长度的时候,一般会使用strlen()函数,但是需要注意的是,strlen计算字符串也是以'\0'结尾的,也就是说,strlen()函数会不断判断当前字符是否为'\0',如果是则立即结束,这个特性和printf函数一样,都是遇到'\0'立即结束:charstr1[]="abc";charstr2[]={'a','\0','c'};charstr3[]={'a','b','c','\0'};char*str4="abc";printf("%d\n",strlen(str1));//输出结果为3printf("%d\n",strlen(str2));//输出结果为1printf("%d\n",strlen(str3));//输出结果为3printf("%d\n",strlen(str4));//输出结果为3以上为c语言中的字符串长度函数。使用时要小心。3、C语言中的字符串复制函数1)strcpy()#includechar*strcpy(char*dest,constchar*src);//功能:将src指向的字符串复制到dest指向的空间中,'\0'也会复制过去参数:dest:目标字符串的首地址src:源字符的首地址返回值:成功:返回dest字符串的首地址失败:NULL代码如下如下:#define_CRT_SECURE_NO_WARNINGS#includecharstr[10]={0};charstr1[]="hello";char*mystr=strcpy(str,str1);将strcpy返回的指针保存到mystr中printf(mystr);memorymodel如下:由于是一个一个复制,也就是说即使在字符串中间遇到'\0'字符,复制也会结束。这里需要注意两个问题:第一,dest指向的内存空间必须足够大,否则可能会出现缓冲区溢出错误;其次,由于strcpy函数本身是一个不安全的函数,编译器会弹出警告,如果你想忽略它,请在程序开头添加宏定义代码:#define_CRT_SECURE_NO_WARNINGS2)strncpy()#includechar*strncpy(char*dest,constchar*src,size_tn);功能:将src指向字符串复制,的前n个字符到dest指向的空格,是否复制结束符看是否指定长度包含“\0”。参数:dest:目标字符串首地址src:源字符首地址n:指定复制的字符串个数返回值:成功:返回dest字符串首地址失败:NULL此函数类似到strcpy,这里并不多余。3)strcat()#includechar*strcat(char*dest,constchar*src);功能:将src字符串连接到dest的末尾,过去也会追加'\0'参数:dest:目的字符串的首地址src:源字符的首地址返回值:成功:返回dest字符串的首地址Failed:NULL这是一个字符串追加函数,将src指向的字符串追加到dest指向的字符串中,同样,也会追加终止符'\0':#define_CRT_SECURE_NO_WARNINGS#includecharstr[]="123";charstr1[]="hello";char*mystr=strcat(str,str1);printf("%s\n%p",mystr,mystr);//输出结果为:123hello但还要注意目标字符串dest必须有足够大的缓冲区才能接收,否则会报错,内存模型如下:4)strncat()#includechar*strncat(char*dest,constchar*src,size_tn);功能:将src字符串的前n个字符连接到dest的末尾,'\0'也会追加过去的参数:dest:目标字符串的首地址src:源字符的首地址n:指定要添加的字符串数量。5)strcmp()#includechar*strcat(char*dest,constchar*src);功能:将src字符串连接到dest的末尾,过去也会追加'\0'参数:dest:目的字符串的首地址src:源字符的首地址返回值:成功:返回deststring的首地址Fail:NULL作用是比较两个字符串的ASCII码,输出不同的结果,常用于判断两个字符串是否相等,示例代码如下:char*str1="helloworld";char*str2="hellomike";if(strcmp(str1,str2)==0){printf("str1==str2\n");}elseif(strcmp(str1,str2)>0){printf("str1>str2\n");}else{printf("str1intstrncmp(constchar*s1,constchar*s2,size_tn);功能:比较s1和s2的前n个字符的大小,比较字符的ASCII码大小。参数:s1:字符串1的首地址s2:字符串2的首地址n:指定比较字符串的个数返回值:等于:0大于:>0小于:<07)sprintf()#includeintsprintf(char*str,constchar*format,...);功能:根据参数格式字符串对数据进行转换格式化,然后将结果输出到str指定的空间,直到出现字符串终止符'\0'。参数:str:字符串首地址format:字符串格式,用法同printf()返回值:success:实际格式化的字符数Fail:-1示例代码如下:chardst[100]={0};inta=10;charsrc[]="hello";intlen=sprintf(dst,"a=%d,src=%s",a,src);printf("dst:%s\n",dst);输出a=10,src=helloprintf("len=%d\n",len);输出14之后,下面会介绍几个字符串操作函数,不过这几个用得比较少:8)sscanf()#includeintsscanf(constchar*str,constchar*format,...);功能:从str指定的字符串中读取数据,并根据参数格式字符串对数据进行转换格式化。参数:str:指定字符串的首地址format:字符串格式,用法同scanf()返回值:success:参数个数,成功转换值的个数失败:-1示例代码:charsrc[]="a=10,b=20";inta;intb;sscanf(src,"a=%d,b=%d",&a,&b);printf("a:%d,b:%d\n",a,b);Output:a:20,b:20sscanf和scanf类似,都是用于输入,只是后者使用屏幕(stdin)作为输入源,前者使用固定的字符串作为输入源。9)strchr()#includechar*strchr(constchar*s,charc);功能:查找字母c在字符串s中出现的位置参数:s:字符串的首地址c:匹配到字母(character)返回值:成功:返回c第一次出现的地址(注意是第地址,而不是字符数组索引)失败:NULL示例代码:charsrc[]="ddda123abcd";char*p=strchr(src,'a');printf("p=%s\n",p);Output:p=a123abcd10)strstr()#includechar*strstr(constchar*haystack,constchar*needle);功能:在字符串haystack中查找字符串needle出现的位置参数:haystack:首地址源字符串needle:匹配字符串的首地址返回值:成功:返回第一次出现的needle地址失败:NULL这个函数和前面的strchr函数类似,只是查找的内容是一个字符串,不是单个字符。11)strtok()#includechar*strtok(c??har*str,constchar*delim);功能:将字符串拆分成几块,当strtok()在参数str的字符串中找到参数delim时,将字符改为\0字符,只将第一个替换成\0当连续有多个时,此函数将破坏原始字符串。参数:str:指向要拆分的字符串delim:拆分字符串中包含的所有字符返回值:成功:拆分字符串的首地址失败:NULL示例代码:chara[100]="www.baidu.com";char*p=strtok(a,".");while(p!=NULL){printf("%s\n",p);p=strtok(NULL,".");}输出:wwwbaiducom上面,这就是本文的全部内容。