(进阶用法) C语言字符串函数

前序

当学习到C语言字符串的操作时,我们知道了字符串的定义方式,字符串的输出方式,其中包括字符串的输入输出语句,例如 puts,gets ,fputs 等。
还有字符串的操作函数, 例如 strcpy,strcmp,strcat,等。到最后明白函数的健壮性之后,学习了strncpy等可以提高代码健壮性的函数。

介绍

在字符串的输出语句使用中,总是离不开 NUL,这个终止符。
NUL ,在C语言每个字符串的中基本都存在,它存在于字符串,却不是字符串的一部分。而在打印相关字符串时,比如 printf 中使用 %s 来打印字符串。 printf 执行时,遇见 NUL 字符便停止执行。

存在问题

  • Strlen 的返回值问题

一般测量字符串长度的时候,我们会使用string.h 库中的strlen
函数来测量。但当看到strlen 的函数定义时,会发现有些许的不一样。
strlen 的函数原型

size_t strlen( char const *string );

其中 size_t 是一个无符号整型类型,定义在 stddef.h 中。

typedef unsigned int size_t

因此, strlen 的返回值永远是一个大于等于0 的整型数。如果你依然不了解这点的重要性,不如来看一段代码。

char str[] = "For text";
if( strlen(str)-10 >= 0 ){
	printf("Alright.\n");
}
else{
	printf("Error.\n");
}

判断这段代码的结果,str 的长度毫无疑问是8,那么执行输出会是“Error” 么?
(进阶用法) C语言字符串函数

遗憾的是,不是。 因为strlen的返回值类型是unsigned int 因此返回值永远不可能大于0。 如果想消除这个问题,将返回值的类型强制转化为 int 类型即可。

  • strcpy 的复制问题
    用于复制字符串的函数通常是 strcpy ,函数原型如下:
char *( char *des ,char const *src );

将字符串 src 的参数赋给 des ,当在 src 中遇见NUL 时,停止赋值。

char str[] = "frank francis";
char text[] = {'t','j','\0','o','h','s','o','n'};
strcpy(str,text);
printf("%s\n",str);

执行结果会是什么?
(进阶用法) C语言字符串函数

因为 text[2] 的值为 '\0’ ,因此在执行时遇见该信号便退出函数。
那么对于strncpy这个函数会不会遇见相同问题呢?答案是肯定的。将上面这则代码中的strcpy 改为strncpy 之后,得到的结构依旧是 “tj”。

推广到其他字符串操作函数,和 strcpy 一样,例如 strcat ,strcmp ,strstr 等依然会在遇见‘\0’之后结束执行。

那么对于中间存在’\0’的字符串要进行操作,如何做呢?

字符串内存操作

当在字符串中遇见 NUL 字符时,无法对 NUL 之后的字符进行处理,这个时候C语言标准库中的定义了一组函数,使得可以对 NUL 字符后面的字符进行操作。
(进阶用法) C语言字符串函数

其中 memcpy 的是复制函数 ,memmove 的功能和 memcpy 差不多,但当复制的源和目标参数可能存在重叠时,就应该使用 memmove

memcmp 为比较函数,功能与 strcpy 相似。

memchr 为字符查找函数,共查找 length 个字符。

memset 为字符设置函数,将 length 之前的所有字符用 ch 进行替代。

这样我们回过头来看第二部分的代码:

char str[] = "frank francis";
char text[] = {'t','j','\0','o','h','s','o','n'};
//strcpy(str,text);
memcpy(str,text,strlen(text));
printf("%s\n",str);

(进阶用法) C语言字符串函数

这样就没有发生使用 strcpy 时的情况。 究其原因,是因为 memcpy 是对内存的操作。在C++ reference 关于 memcpy 介绍中:
(进阶用法) C语言字符串函数
而对于 strcpy则是:
(进阶用法) C语言字符串函数

当然,字符串中遇见 NUL 的情况时很少见的,因此使用 strcpy 基本可以满足日常的需求。

总结

strcpy 的定义是,字符串赋值函数,而在“mem”系列中的函数,参数以及函数的类型都是 void ,因此可以通过“mem” 系列函数来进行对不同类型变量或者结构的操作。