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

旷世的忧伤

不与夏虫语寒,不与曲人语道,因为生命缺乏言说的条件......

 
 
 

日志

 
 

C语言中的字符串拷贝函数  

2015-01-05 22:28:20|  分类: C语言 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、strcpy与strncpy
原型:

#include <string.h>

char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);

strcpy在拷贝字符串时会把结尾的'\0'也拷到dest中,因此保证了dest中是以'\0'结尾的字符串。但另外一个要注意的问题是,strcpy只知道src字符串的首地址,不知道长度,它会一直拷贝到'\0'为止,所以dest所指向的内存空间要足够大,否则有可能写越界。例如:

char buf[10];
strcpy(buf, "hello world");

如果没有保证src所指向的内存空间以'\0'结尾,也有可能读越界,例如:

char buf[10] = "abcdefghij", str[4] = "hell";
strcpy(buf, str);

因为strcpy函数的实现者通过函数接口无法得知src字符串的长度和dest内存空间的大小,所以“确保不会写越界”应该是调用者的责任,调用者提供的dest参数应该指向足够大的内存空间,“确保不会读越界”也是调用者的责任,调用者提供的src参数指向的内存应该确保以'\0'结尾。

特别要注意的是,src和dest所指向的内存空间不能有重叠,这是本文要强调的重点之一。凡是有指针参数的C标准库函数基本上都有这条要求,每个指针参数所指向的内存空间互不重叠,例如这样调用是不允许的:

char buf[10] = "hello";
strcpy(buf, buf+1);

这样的程序不会在编译的时候报错或者提示警告,但是在程序运行的时候会出现无法预知的错误,偶尔 程序可能也会正常运行。

strncpy
的参数n指定最多从src中拷贝n个字节到dest中,换句话说,如果拷贝到'\0'就结束,如果拷贝到n个字节还没有碰到'\0',那么也结束,调用者负责提供适当的n值,以确保读写不会越界,比如让n的值等于dest所指向的内存空间的大小:

char buf[10];
strncpy(buf, "hello world", sizeof(buf));


在manpage中特意给出了strncpy的原型实现:

char *strncpy(char *dest, const char *src, size_t n)
{
  size_t i;

  for (i = 0; i < n && src[i] != '\0'; i++)
    dest[i] = src[i];
  for ( ; i < n; i++)
    dest[i] = '\0';
  return dest;
}

我想,这是要告诉读者一个事实,dest有可能不是以'\0'结尾的,这是本文要强调的第二个重点。例如上面的调用,虽然把"hello world"截断到10个字符拷贝至buf中,但buf不是以'\0'结尾的,如果再printf(buf)就会读越界。如果你需要确保dest以'\0'结束,可以这么调用:

char buf[10];
strncpy(buf, "hello world", sizeof(buf));
buf[sizeof(buf)-1] = '\0';

也就是说,在调用strncpy后,最好手动加上'\0'结束符。strncpy还有一个特性,如果src字符串全部拷完了不足n个字节,那么还差多少个字节就补多少个'\0',但是正如上面所述,这并不保证dest一定以'\0'结束,当src字符串的长度大于n时,不但不补多余的'\0',连字符串的结尾'\0'也不拷贝。

二、memcpy与memmove
原型:

#include <string.h>


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

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

memcpy函数从src所指的内存地址拷贝n个字节到dest所指的内存地址,和strncpy不同,memcpy并不是遇到'\0'就结束,而是一定会拷贝完n个字节。这里的命名规律是,以str开头的函数处理以'\0'结尾的字符串,而以mem开头的函数则不关心'\0'字符,或者说这些函数并不把参数当字符串看待,因此参数的指针类型是void *而非char *。

memmove也是从src所指的内存地址拷贝n个字节到dest所指的内存地址,虽然叫move但其实也是拷贝而非移动。但是和memcpy有一点不同,memcpy的两个参数src和dest所指的内存区间如果重叠则无法保证正确拷贝,而memmove却可以正确拷贝。假设定义了一个数组charbuf[20] = "hello world\n";,如果想把其中的字符串往后移动一个字节(变成"hhello world\n"),调用memcpy(buf + 1, buf, 13)是无法保证正确拷贝的。例:(错误的memcpy调用)

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

int main(void)
{
char buf[20] = "hello world\n";
memcpy(buf + 1, buf, 13);
printf(buf);
return 0;
}

这样的程序在不同的机器上会出现不同的结果,即结果是未知的。如果把代码中的memcpy改成memmove则可以保证正确拷贝。

本文内容参考《Linux C编程一站式学习》
  评论这张
 
阅读(2)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

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

页脚

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