文章目录
- 1. 获取字符串长度
- strlen
- 2. 复制字符串
- strcpy
- strncpy
- 3. 拼接字符串
- strcat
- strncat
- 4. 字符串比较
- strcmp
- strncmp
1. 获取字符串长度
strlen
库函数strlen的原型如下:
size_t strlen( char const *string );
注意strlen
返回一个类型为size_t
的值。这个类型是在头文件stddef.h中定义的,它是一个无符号整数类型。在表达式中使用无符号数可能导致不可预料的结果。例如,下面两个表达式看上去是一样的:
if( strlen( x ) >= strlen( y ) ) ...
if( strlen( x ) – strlen( y ) >= 0 ) ...
但事实上它们是不相等的。第1条语句将按照你预想的那样工作,但第2条语句的结果将永远是真。strlen
的结果是个无符号数,所以操作符>=左边的表达式也将是无符号数,而无符号数绝不可能是负的。
表达式中如果同时包含了有符号数和无符号数,可能会产生奇怪的结果。和前一对语句一样,下面两条语句并不相等,其原因相同。
if( strlen( x ) >= 10 ) ...
if( strlen( x ) – 10 >= 0 ) ...
如果把strlen
的返回值强制转换为int,就可以消除这个问题。
2. 复制字符串
strcpy
用于复制字符串的函数是strcpy
,它的原型如下所示:
char *strcpy( char *dst, char const *src );
这个函数把参数src字符串复制到dst参数。如果参数src和dst在内存中出现重叠,其结果是未定义的。由于dst参数将进行修改,所以它必须是个字符数组或者是一个指向动态分配内存的数组的指针,不能使用字符串常量。该函数返回它们第1个参数的一份拷贝,就是一个指向目标字符数组的指针。
目标参数的以前内容将被覆盖并丢失。即使新的字符串比dst原先的内存更短,由于新字符串是以NUL字节结尾,所以老字符串最后剩余的几个字符也会被有效地删除。举个例子来说:
char message[] = "Original message";
...
if (...)strcpy(message, "Different");
如果条件为真并且复制顺利执行,数组将包含下面的内容:
第1个NUL字节后面的几个字符再也无法被字符串函数访问,因此从任何现实的角度看,它们都已经是丢失的了。
程序员必须保证目标字符数组的空间足以容纳需要复制的字符串。如果字符串比数组长,多余的字符仍被复制,它们将覆盖原先存储于数组后面的内存空间的值。strcpy无法解决这个问题,因为它无法判断目标字符数组的长度。例如:
```c
char message[] = "Original message";
...
if (...)strcpy(message, "A different message");
第2个字符串太长了,无法容纳于message字符数组中。因此,strcpy
函数将侵占数组后面的部分内存空间,改写原先恰好存储在那里的变量。这可能发生意想不到的效果。如果在使用这个函数前确保目标参数足以容纳源字符串,就可以避免大量的调试工作。
strncpy
和strcpy
一样,strncpy
把源字符串的字符复制到目标数组。然而,它总是正好向dst写入len个字符。它的原型如下:
char *strncpy(char *dst, char const *src, size_t len);
如果strlen( src )的值小于len,dst数组就用额外的NUL字节填充到len长度。如果strlen( src )的值大于或等于len,那么只有len个字符被复制到dst中。注意!它的结果将不会以NUL字节结
尾 。
strncpy调用的结果可能不是一个字符串,因此字符串必须以NUL字节结尾。如果在一个需要字符串的地方(例如strlen函数的参数)使用了一个不是以NUL字节结尾的字符序列,会发生什么情况呢?strlen函数将无法知道NUL字节是没有的,所以它将继续进行查找,一个字符接一个字符,直到它发现一个NUL字节为止。或许它找了几百个字符才找到,而strlen函数的这个返回值从本质上说是一个随机数。或者,如果函数试图访问系统分配给这个程序以外的内存范围,程序就会崩溃。
这个问题只有当你使用strncpy函数创建字符串,然后或者对它们使用str开头的库函数,或者在printf中使用%s格式码打印它们时才会发生。在使用不受限制的函数之前,你首先必须确定字符串实际上是以NUL字节结尾的。例如,考虑下面这个代码段:
char buffer[BSIZE];
...
strncpy(buffer, name, BSIZE);
buffer[BSIZE - 1]=`\0';
如果name的内容可以容纳于buffer中,最后那个赋值语句没有任何效果。但是,如果name太
长,这条赋值语句可以保证buffer中的字符串是以NUL结尾的。以后对这个数组使用strlen或其
他不受限制的字符串函数将能够正确工作。
3. 拼接字符串
strcat
要想把一个字符串添加(连接)到另一个字符串的后面,你可以使用strcat函数。它的原型如下:
char *strcat( char *dst, char const *src );
strcat函数要求dst参数原先已经包含了一个字符串(可以是空字符串)。它找到这个字符串的末尾,并把src字符串的一份拷贝添加到这个位置。如果src和dst的位置发生重叠,其结果是未定义的。该函数返回它们第1个参数的一份拷贝,就是一个指向目标字符数组的指针。
strcpy(message, "Hello ");
strcat(message, customer_name);
strcat(message, ", how are you?");
每个strcat
函数的字符串参数都被添加到原先存在于message数组的字符串后面。其结果是下面这个字符串:
Hello Jim, how are you?
和前面一样,程序员必须保证目标字符数组剩余的空间足以保存整个源字符串。但这次并不是简单地把源字符串的长度和目标字符数组的长度进行比较,必须考虑目标数组中原先存在的字符串。
我们也可以这样嵌套,因为返回值是第一个参数的指针:
strcat( strcpy( dst, a ), b );
strcpy首先执行。它把字符串从a复制到dst并返回dst。然后这个返回值成为strcat函数的第1个参数,strcat函数把b添加到dst的后面。
它等价于:
strcpy( dst, a );
strcat( dst, b );
strncat
尽管strncat也是一个长度受限的函数,但它和strncpy存在不同之外。它从src中最多复制len个字符到目标数组的后面。它的原型如下:
char *strcat( char *dst, char const *src );
但是,strncat总是在结果字符串后面添加一个NUL字节,而且它不会像strncpy那样对目标数组用NUL字节进行填充。注意目标数组中原先的字符串并没有算在
strncat的长度中。strncat最多向目标数组复制len个字符(再加一个结尾的NUL字节),它才不管目标参数除去原先存在的字符串之后留下的空间够不够。
4. 字符串比较
strcmp
比较两个字符串涉及对两个字符串对应的字符逐个进行比较,直到发现不匹配为止。那个最先不匹配的字符中较“小”(也就是说,在字符集中的序数较小)的那个字符所在的字符串被认为“小于”另外一个字符串。如果其中一个字符串是另外一个字符串的前面一部分,那么它也被认为“小于”另外一个字符串,因为它的NUL结尾字节出现得更早。这种比较被称为“词典比较”,对于只包含大写字母或只包含小写字母的字符串比较,这种比较过程所给出的结果总是和我们日常所用的字母顺序的比较相同。
库函数strcmp
用于比较两个字符串,它的原型如下:
int strcmp( char const *s1, char const *s2 );
如果s1小于s2,strcmp函数返回一个小于零的值。如果s1大于s2,函数返回一个大于零的值。如果两个字符串相等,函数就返回零。注意标准并没有规定用于提示不相等的具体值。它只是说如果第1个字符串大于第2个字符串就返回一个大于零的值,如果第1个字符串小于第2个字符串就返回一个小于零的值。一个常见的错误是以为返回值是1和-1,分别代表大于和小于。但这个假设并不总是正确的。
对于这个函数常出现的错误是:
if( strcmp( a, b ) )
他以为如果两个字符串相等,它的结果将是真。但是,这个结果将正好相反,因为在两个字符串相等的情况下返回值是零(假)。然而,把这个返回值当作布尔值进行测试是一种坏风格,因为它具有三个截然不同的结果:小于、等于和大于。所以,更好的方法是把这个返回值与零进行比较。
由于strcmp并不修改它的任何一个参数,所以不存在溢出字符数组的危险。但是,和其他不受限制的字符串函数一样,strcmp函数的字符串参数也必须以一个NUL字节结尾。如果并非如此,strcmp就可能对参数后面的字节进行比较,这个比较结果将不会有什么意义。
strncmp
strncmp也用于比较两个字符串,但它最多比较len个字节。它的原型为:
int strncmp( char const *s1,char const *s2,size_t len );
如果两个字符串在第len个字符之前存在不相等的字符,这个函数就像strcmp一样停止比较,返回结果。如果两个字符串的前len个字符相等,函数就返回零。
参考
《C和指针》