2.3 标准库提供的字符串处理函数

C/C++提供了众多的字符串处理函数,表2-1介绍了主要的字符串处理函数。

表2-1 字符串处理函数

传递给这些标准库函数例程的指针必须具有非零值,并且指向以null结束的字符数组中的第一个元素。其中一些标准库函数会修改传递给它的字符串,这些函数将假定它们所修改的字符串具有足够大的空间接收本函数新生成的字符,程序员必须确保目标字符串必须足够大。

2.3.1 strlen

strlen()计算字符数组的字符数,以'\0'为结束标志,计算不为'\0'的数组元素个数。

自定义函数实现strlen的功能是:

            int strlen(const char *str){
                assert(str != NULL);
                int len=0;
                while((*str++) != '\0')
                    len++;
                return len;
            }

或不用变量实现此功能

            int strlen(const char *str){
                assert(str != NULL);
                return *str == '\0' ? 0 : (1+strlen(++str));
            }

例1:下面的程序的输出是什么?(2010·网易)

            int main(void){
                int n;
                char y[10]="ntse";
                char *x=y;
                n=strlen(x);
                *x=x[n];
                x++;
                printf("x=%s\n", x);
                printf("y=%s\n", y);
                return 0;
            }

解答:该程序的输出结果为:

            x=tse
            y=

因为n=4,则语句“*x=x[n];”的功能是将x指向的第一个字符'n'修改为'\0',这样y字符串的第一位为'\0',所以第二个输出为空;进行x++操作后,x指向第二个字符t,所以第一个输出为:tse。

注意:printf语句在输出字符串时,将'\0'当作字符串的结尾。

2.3.2 strcmp

即两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。如:

            "A" < "B"
            "a" > "A"
            "computer" > "compare"

自定义函数实现strcmp()函数的功能:

            int strcmp (const char * str1, const char * str2){
                assert(str1 != NULL && str2 != NULL);
                int ret=0;
                while(!(ret=*(unsigned char *)str1- *(unsigned char *)str2) && * str1){
                    str1++;//++str1;
                    str2++;//++str2;
                }
                if(ret<0)           ret=-1;
                else if(ret>0)      ret=1;
                return ret;
            }

例1:判断字符串a和b是否相等,应当使用(  )?(2012·迅雷)

A.if(a==b)

B.if(a=b)

C.if(strcpy(a,b))

D.if(strcmp(a, b))

解答:D。A比较的是首地址而不是内容。

2.3.3 strcat与strcpy

strcat(dest,src)把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。

strcpy(dest,src)把从src地址开始且含有null结束符的字符串复制到以dest开始的地址空间。

传递给标准库函数strcat和strcpy的第一个实参数组必须具有足够大的空间存放新生成的字符串。

自定义函数实现strcat()函数的功能:

            char *strcat(char *strDest, const char *strSrc){
                char *address=strDest;
                assert((strDest != NULL) && (strSrc != NULL));
                while(*strDest){
                    strDest++;
                }
                while(*strDest++=*strSrc++);
                return address;
            }

自定义函数实现strcpy()函数的功能:

            char *strcpy(char *strDestination, const char *strSource){
                assert(strDestination != NULL && strSource != NULL);
                char *strD=strDestination;
                while ((*strDestination++=*strSource++) != '\0');
                return strD;
            }

strcat与strncat的区别是strncat是将s2的前n个字符连接到s1后面,并返回s1,也即其是利用n作为结束标志,而strcat是利用字符串末尾的空字符作为结束标志。strcpy与strncpy的原理同。

例1:以下哪个说法正确 ?(2012·搜狗)

            int func(){
                char b[2]={0};
                strcpy(b, "aaa");
            }

A.Debug版崩溃,Release版正常

B.Debug版正常,Release版崩溃

C.Debug版崩溃,Release版崩溃

D.Debug版正常,Release版正常

解答:A。因为在Debug中有ASSERT断言保护,所以要崩溃,而在Release中就会删掉ASSERT,所以会出现正常运行。但是不推荐如此做,因为这样会覆盖不属于自己的内存,这极易导致程序崩溃。

2.3.4 memcpy与memset

1.memcpy

            void *memcpy(void *dest, const void *src, size_t n);

功能:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。函数返回指向dest的指针。

例1:下面的代码可能出现下列哪些运行结果(  )?【多选】(2012·中兴)

            void MyFunc(unsigned char * pucData, unsigned short wLen){
                unsigned char aucBuff[MAX_BUFF_LEN];
                memcpy(aucBuff, pucData, wLen);
            }

A.程序运行正常

B.某个全局变量被写覆盖

C.aucBuff数组写越界

D.程序跑飞

解答:AC。aucBuff的内存在栈上,而全局变量存储在全局(静态)存储区,不可能被覆盖,故B错。

程序“跑飞”是指系统受到某种干扰后,程序计数器PC的值偏离了给定的唯一变化历程,导致程序运行偏离正常的运行路径。程序“跑飞”因素及后果往往是不可预计的。在很多情况下,程序“跑飞”后系统会进入死循环而导致死机,本题代码不会造成这种后果,故D错。

例2:请写出strcpy和memcpy的区别?(2012·海康威视)

解答:strcpy和memcpy都是标准C库函数,主要有以下3方面的区别:

1)复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符。memcpy对于需要复制的内容没有限制,因此用途更广。

2)复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符'\0'时才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

3)用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。

2.memset

            void *memset(void *s, int ch, size_t n);

功能:将s中前n个字节用ch替换并返回s,作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。