肯尼斯·里科《C和指针》第9章 字符串、字符和字节(1)

主要是肯尼斯·里科《C和指针》第6章 指针(7)编程的练习:删除字符-CSDN博客这个一直还没有想到解决的办法,打算学习下第9章的字符串、字符和字节,看看对解决这个问题有没有帮助吧。

9.1 字符串基础

首先回顾一下字符串的基础知识。字符串就是一串零个或多个字符,并且以一个位模式为全0的NUL字节结尾。因此,字符串所包含的字符内部不能出现NUL字节。这个限制很少会引起问题,因为NUL字节并不存在与它相关联的可打印字符,这也是选它为终止符的原因。NUL字节是字符串的终止符,但它本身并不是字符串的一部分,所以字符串的长度并不包括NUL字节。

头文件string.h包含了使用字符串函数所需的原型和声明。尽管并非必需,但在程序中包含这个头文件确实是个好主意,因为有了它所包含的原型,编译器可以更好地为程序执行错误检查。

9.2 字符串长度

字符串的长度就是它所包含的字符个数。我们很容易通过对字符进行计数来计算字符串的长度,程序9.1就是这样做的。这种实现方法说明了处理字符串所使用的处理过程的类型。但是,事实上我们极少需要编写字符串函数,因为标准库所提供的函数通常都能完成这些任务。不过,如果还是希望自己编写一个字符串函数,请注意标准保留了所有以str开头的函数名,用于标准库将来的扩展。

/*
** 计算字符串参数的长度。
*/
#include <stddef.h>
size_t
strlen( char const *string )
{
   int length;
   for( length = 0; *string++ != '\0'; )
       length += 1;
   return length;
}

库函数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,就可以消除这个问题。

批注:返回的竟然是size_t,我还以为返回的是int。

9.3 不受限制的字符串函数

最常用的字符串函数都是“不受限制”的,也就是说它们只是通过寻找字符串参数结尾的NUL字节来判断它的长度。这些函数一般都指定一块内存用于存放结果字符串。在使用这些函数时,程序员必须保证结果字符串不会溢出这块内存。本节在具体讨论每个函数时,将对这个问题做更详细的讨论。

9.3.1 复制字符串

用于复制字符串的函数是strcpy,它的原型如下所示:

char    *strcpy( char *dst, char const *src );

这个函数把参数src字符串复制到dst参数。如果参数src和dst在内存中出现重叠,其结果是未定义的。由于dst参数将进行修改,因此它必须是个字符数组或者是一个指向动态分配内存的数组的指针,不能使用字符串常量。这个函数的返回值将在9.3.3节描述。

目标参数的以前内容将被覆盖并丢失。即使新的字符串比dst原先的内存更短,由于新字符串是以NUL字节结尾,所以老字符串最后剩余的几个字符也会被有效地删除

程序员必须保证目标字符数组的空间足以容纳需要复制的字符串。如果字符串比数组长,多余的字符仍被复制,它们将覆盖原先存储于数组后面的内存空间的值。strcpy无法解决这个问题,因为它无法判断目标字符数组的长度。

例如:

char      message[] = "Original message";
...
strcpy( message, "A different message" );

第2个字符串太长了,无法容纳于message字符数组中。因此,strcpy函数将侵占数组后面的部分内存空间,改写原先恰好存储在那里的变量。如果在使用这个函数前,确保目标参数足以容纳源字符串,就可以避免大量的调试工作。

批注:没讲具体如何实现的啊,,,

9.3.2 连接字符串

要想把一个字符串添加(连接)到另一个字符串的后面,可以使用strcat函数。它的原型如下:

char    *strcat( char *dst, char const *src );

strcat函数要求dst参数原先已经包含了一个字符串(可以是空字符串)。它找到这个字符串的末尾,并把src字符串的一份副本添加到这个位置。如果src和dst的位置发生重叠,其结果是未定义的。

下面这个例子显示了这个函数的一种常见用法:

strcpy(message, "Hello ");
strcat(message, "Jim,");
strcat(message, " how are you?");

//Hello Jim, how are you? 

和前面一样,程序员必须保证目标字符数组剩余的空间足以保存整个源字符串。但这次并不是简单地比较源字符串的长度和目标字符数组的长度,因此必须考虑目标数组中原先存在的字符串。

9.3.3 函数的返回值

strcpy和strcat都返回它们第1个参数的一份副本,就是一个指向目标字符数组的指针。由于它们返回这种类型的值,因此可以嵌套地调用这些函数,如下面的例子所示

strcat( strcpy( dst, a ), b );

strcpy首先执行。它把字符串从a复制到dst并返回dst。然后这个返回值成为strcat函数的第1个参数,strcat函数把b添加到dst的后面。

这种嵌套调用的风格较之下面这种可读性更佳的风格在功能上并无优势:

strcpy( dst, a );
strcat( dst, b );

事实上,在这些函数的绝大多数调用中,它们的返回值只是被简单地忽略。

9.3.4 字符串比较

比较两个字符串涉及对两个字符串对应的字符逐个进行比较,直到发现不匹配为止。那个最先不匹配的字符中较“小”(也就是说,在字符集中的序数较小)的那个字符所在的字符串被认为“小于”另外一个字符串。如果其中一个字符串是另外一个字符串的前面一部分,那么它也被认为“小于”另外一个字符串,因为它的NUL结尾字节出现得更早。这种比较被称为“词典比较”,对于只包含大写字母或只包含小写字母的字符串比较,这种比较过程所给出的结果总是和我们日常所用的字母顺序的比较相同

库函数strcmp用于比较两个字符串,它的原型如下:

int   strcmp( char const *s1, char const *s2 );

如果s1小于s2,strcmp函数返回一个小于零的值;如果s1大于s2,函数返回一个大于零的值;如果两个字符串相等,函数就返回零

初学者常常会编写下面这样的表达式:

if( strcmp( a, b ) )

他以为如果两个字符串相等,它的结果将是真。但是,这个结果将正好相反,因为在两个字符串相等的情况下返回值是零(假)。然而,把这个返回值当作布尔值进行测试是一种坏风格,因为它具有3个截然不同的结果:小于、等于和大于。所以,更好的方法是把这个返回值与零进行比较。

注意,标准并没有规定用于提示不相等的具体值。它只是说如果第1个字符串大于第2个字符串,就返回一个大于零的值;如果第1个字符串小于第2个字符串,就返回一个小于零的值。一个常见的错误是以为返回值是1和−1,分别代表大于和小于,但这并不总是正确的。

由于strcmp并不修改它的任何一个参数,因此不存在溢出字符数组的危险。但是,和其他不受限制的字符串函数一样,strcmp函数的字符串参数也必须以一个NUL字节结尾。如果并非如此,strcmp就可能对参数后面的字节进行比较,这个比较结果将不会有什么意义

9.4 长度受限的字符串函数

标准库还包含了一些函数,它们以一种不同的方式处理字符串。这些函数接受一个显式的长度参数,用于限定进行复制或比较的字符数。这些函数提供了一种方便的机制,可以防止难以预料的长字符串从它们的目标数组溢出

这些函数的原型如下所示。和它们的不受限制版本一样,如果源参数和目标参数发生重叠,strncpy和strncat的结果就是未定义的。

char *strncpy(char *dst, char const *src, size_t len);
char *strncat(char *dst, char const *src, size_t len);
int  stncmp(char const *s1, char const *s2, size_t len);

和strcpy一样,strncpy把源字符串的字符复制到目标数组。然而,它总是正好向dst写入len个字符。如果strlen( src)的值小于len,dst数组就用额外的NUL字节填充到len长度;如果strlen(src)的值大于或等于len,那么只有len个字符被复制到dst中。注意!它的结果将不会以NUL字节结尾

尽管strncat也是一个长度受限的函数,但它和strncpy存在不同之外。它从src中最多复制len个字符到目标数组的后面。但是,strncat总是在结果字符串后面添加一个NUL字节,而且它不会像strncpy那样对目标数组用NUL字节进行填充。注意,目标数组中原先的字符串并没有算在strncat的长度中。strncat最多向目标数组复制len个字符(再加一个结尾的NUL字节),它才不管目标参数除去原先存在的字符串之后留下的空间够不够。

最后,strncmp也用于比较两个字符串,但它最多比较len个字节。如果两个字符串在第len个字符之前存在不相等的字符,这个函数就像strcmp一样停止比较,并返回结果;如果两个字符串的前len个字符相等,函数就返回零。

  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值