注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

信息 灵感 创新

I? =Information,Inspiration,Innovation

 
 
 

日志

 
 
关于我

we are 5. Mathematics, Computation, Programming, Engineering, and Making fun of life.

网易考拉推荐

头文件string.h中的函数(3)  

2014-06-11 09:40:17|  分类: C & C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

4. 字符串复制

string.h下有好几个版本的字符串复制函数,最常见的就是strcpy,函数原型如下:

char* strcpy(char* destination, const char* source);
函数将原字符串中包括’\0’的字符,复制到目标字符数组中,函数返回目标字符数组的首地址。
在使用这个函数的时候,要特别留心目标字符串的长度要足够长,能够容纳源字符串中的所有内容,看看下面的测试代码:
#include <stdio.h>
#include <string.h>
int main ()
{
    char str[]="nice to meet you!";
    char destStr1[20];
    char destStr2[10];
    strcpy(destStr1,str);
    strcpy(destStr2,str);//length of this array is not enough
    printf("%d %d\n",strlen(destStr1),sizeof(destStr1));
    printf("%d %d\n",strlen(destStr2),sizeof(destStr2));
    puts(destStr1);
    puts(destStr2);
    return 0;
}
程序执行的结果是:

17 20
17 10
nice to meet you!
nice to meet you!

这里反映了两个问题:
1.strcpy函数在目标字符数组长度不够容纳源字符串中内容时,并不会截断,而是继续填充,直到碰到源字符串的’\0’才结束,这就导致了越界访问,可能破坏了程序的数据。
2.strlen和sizeof的行为不一样,对于数组,sizeof返回的是申明的长度,但是strlen则是根据’\0’确定字符串长度的。
为了避免第一个问题,在string.h中还有另外两个字符串复制的函数,strcpy_s和strncpy。
strcpy_s函数中的s表示secure,即安全的,如果将第二个字符串复制函数替换为strcpy_s,则在运行时会出现如下错误:
CX_0605_200205
虽然这是一个简单粗暴的解决方式,但是至少能保证其执行结果是正确的(或者说,不能保证做对的时候就什么也不做)。
另一个函数strncpy是最多复制n个字符到目标函数中去,如果碰到了’\0’就不再复制了,但是这个函数只能说是比strcpy强了一点,如果使用不当,还是会和strcpy一样的效果。看看下面的代码:
#include <stdio.h>
#include <string.h>
int main ()
{
    char str[]="nice to meet you!";
    char destStr2[10];
    strncpy(destStr2,str,10);
    printf("%d %d\n",strlen(destStr2),sizeof(destStr2));
    puts(destStr2);
    return 0;
}
由于str中是有17个字符,第10个字符是’e’,使用strncpy之后,看看执行的结果:
image

为什么是这个结果呢(实际上结果是不确定的)?因为我们复制的时候,由于复制的10个字符都不是’\0’,因此数组destStr2中的字符分别是nice to me,这样,在使用strlen的时候,由于没有碰到期望中的’\0’,指针会一直走下去,直到碰到一个’\0’。从调试过程中也能发现这个问题:

image

请注意上图中的那个红色的圈,这是strlen碰到的第一个’\0;,但是这已经不是我们所期望的结果了。因此,对这个函数的误用,也会带来一些问题,使用时也要小心,因为许多人都容易忽视’\0’。

最后说说memcpy和memmove这两个函数,根据前面的说明,他们更多地是内存操作函数,而不是仅用于字符串的操作函数。memcpy函数原型如下:

void* memcpy (void* destination, const void* source, size_t num);
可以把它理解为内存复制函数,注意第三个参数是字节数,不一定是元素个数,而且返回的是destination地址,类型为void*,因此需要进行类型转换。测试代码如下:
int datas[10]={1,2,3,4,5,6,7,8,9,10};
int destDat[10];
memcpy(destDat,datas,sizeof(destDat)*sizeof(int));
int i=0;
for(i=0;i<10;i++)
    printf("%d\n",destDat[i]);
这段代码将实现数组的复制,请注意最后一个参数。
对于字符串操作,使用是类似的,但是需要注意,如果对字节的长度控制没有把握好,同样会带来strncpy的问题,而且这个函数即使碰到了’\0’的时候,如果没有达到指定的字节数,还会继续复制下去,这种错误我就不演示了。
memmove是内存移动函数,函数原型如下:
void* memmove (void* destination, const void* source, size_t num);

可以看到,参数列表和memcpy是一致的,实际上,当源字符串和目标字符串的地址没有发生重叠时,函数执行的效果是一样的,如果有重叠时,memcpy的行为是未定义的。而memmove有个好处是,它运行起来像是有一个临时缓存一样,导致目标指针和源字符串指针可以重叠,这中行为可以带来一些比较有趣的效果。例如,我们想把一个字符串中的每一个字母进行循环移位,例如对字符数组chArr[n]循环左移位,将最后一个字符(不包含’\0’)移动到倒数第二位,依此类推,第二个字符移动到第一位,而第一位则放到最后面。实现函数如下:

#include <stdio.h>
#include <string.h>

void StrShiftLeft(char* chArr)
{
    int n=strlen(chArr);
    char c=*chArr;
    memmove(chArr,chArr+1,(n-1)*sizeof(char));
    chArr[n-1]=c;
}

int main ()
{
    char str[]="nice to meet you!";
    puts(str);
    int i,x=strlen(str);
    for(i=0;i<x;i++)
    {
        StrShiftLeft(str);
        puts(str);
    }    
    return 0;
}

运行效果如下:

nice to meet you!
ice to meet you!n
ce to meet you!ni
e to meet you!nic
to meet you!nice
to meet you!nice
o meet you!nice t
meet you!nice to
meet you!nice to
eet you!nice to m
et you!nice to me
t you!nice to mee
you!nice to meet
you!nice to meet
ou!nice to meet y
u!nice to meet yo
!nice to meet you
nice to meet you!

这种整段移动的技巧,很多时候能避免编写循环语句,还有就是在不能确定地址是否有重叠的时候,请使用memmove,虽然memcpy的效率比memmove高。另外微软的VC中还提供了memcpy和memmove对应的安全版本memcpy_s和memmove_s,这里就不做介绍了。

  评论这张
 
阅读(560)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017