头文件:#include <string.h>

strcpy() 函数用来复制字符串,其原型为:

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

【参数】dest 为目标字符串指针,src 为源字符串指针。

注意:src 和 dest 所指的内存区域不能重叠,且dest 必须有足够的空间放置 src 所包含的字符串(包含结束符NULL)

【返回值】成功执行后返回目标数组指针 dest。

strcpy() 把src所指的由NULL结束的字符串复制到dest 所指的数组中,返回指向 dest 字符串的起始地址。

注意:如果参数
dest 所指的内存空间不够大,可能会造成缓冲溢出(buffer Overflow)的错误情况,在编写程序时请特别留意,或者用strncpy()来取代。

strncpy()用来复制字符串的前n个字符,其原型为:

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

【参数说明】dest 为目标字符串指针,src 为源字符串指针。

strncpy()会将字符串src前n个字符拷贝到字符串dest。

不像strcpy(),strncpy()不会向dest追加结束标记'\0',这就引发了很多不合常理的问题,将在下面的示例中说明。

如果src的前n个字节不含NULL字符,则结果不会以NULL字符结束。        

如果src的长度小于n个字节,则以NULL填充dest直到复制完n个字节。        

src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。    

注意:src 和 dest 所指的内存区域不能重叠,且dest 必须有足够的空间放置n个字符

【返回值】返回指向dest的指针(该指向dest的最后一个元素)

1. strcpy

我们知道,strcpy 是依据 \0 作为结束判断的,如果 to 的空间不够,则会引起 buffer overflow。

strcpy 常规的实现代码如下(来自 OpenBSD 3.9):

char *

strcpy(char *to, const char *from)

{

       char *save = to;

for (; (*to = *from) != '\0'; ++from, ++to);

       return(save);

}

但通常,我们的 from 都来源于用户的输入,很可能是非常大的一个字符串,因此 strcpy 不够安全。

2. strncpy

在 ANSI C 中,strcpy 的安全版本是 strncpy。

char *strncpy(char *s1, const char *s2, size_t n);

但 strncpy 其行为是很诡异的(不符合我们的通常习惯)。标准规定 n 并不是 sizeof(s1),而是要复制的 char 的个数。一个最常见的问题,就是 strncpy 并不帮你保证 \0

结束。

char buf[8];

strncpy( buf, "abcdefgh", 8 );

看这个程序,buf 将会被 "abcdefgh" 填满,但却没有 \0 结束符了。

另外,如果 s2 的内容比较少,而 n 又比较大的话,strncpy 将会把之间的空间都用 \0 填充。这又出现了一个效率上的问题,如下:

char buf[80];

strncpy( buf, "abcdefgh", 79 );

上面的 strncpy 会填写 79 个 char,而不仅仅是 "abcdefgh" 本身。

strncpy 的标准用法为:(手工写上 \0)

strncpy(path, src, sizeof(path) - 1);

path[sizeof(path) - 1] = '\0';

len = strlen(path);

--------------------------------------------------------------------------

注意:定义一个 字符串数组的时候,一定要留一个字节给空字符'\0'。

char qq[8] = "qwer123"

char ww[8] = "qwer1234"

printf("%s\n",qq);

printf("%s\n",ww);

执行这条语句后,打印的结果都是一样的。都是  qwer123 ,因为 ww[8]的最后一个字节被'\0'填充了。

还有一些讲了strncpy注意事项的博客,写的不错。

http://blog.csdn.net/stpeace/article/details/22581763  (别再耍流氓了: 请别再用strcpy, 而用strncpy)

http://blog.haipo.me/?p=1065             (停止使用 strncpy 函数!)(这篇文章重点看)

停止使用 strncpy 函数!

也许你曾经被多次告知,要使用 strncpy 替代 strcpy 函数,因为 strncpy 函数更安全。而今天我要告诉你,strncpy 是不安全的,并且是低效的,strncpy 的存在是由于一个历史原因造成的,你不应当再使用 strncpy 函数。

下面我来解释为什么 strncpy 函数是不安全并且是低效的,以及我们应该使用那些替代函数。

我以前对 strncpy 一直存在误解,直到有一次出现了 BUG。好不容易定位到 strncpy 身上,然后仔细查看文档,才明白问题所在。

误解一:如果src 长度小于 n, 那么strncpy 和 strcpy 效果一样?

错,事实上,strncpy 还会把 dest 剩下的部分全部置为 0

一直认为 strncpy 只是比 strcpy 多了长度校验,却不知道 strncpy 会把剩下的部分全置为 0(粗体部分)。

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

DESCRIPTION

The strcpy() function copies the string pointed to by src (including the terminating `\0′ character) to the array pointed to by dest. The strings may

not overlap, and the destination string dest must be large enough to receive the copy.

The strncpy() function is similar, except that not more than n bytes of src are copied. Thus, if there is no null byte among the first n bytes of src,

the result will not be null-terminated.

In the case where the length of src is less than that of n, the remainder of dest will be padded with null bytes.

这会导致什么后果呢?

首先,如果 strncpy 的长度填错了,比如比实际的长,那么就可能会把其他数据清 0 了。我就遇到过这个问题,在后来检查代码看到这个问题时,也并不以为然,因为拷贝的字符串不可能超过缓冲区的长度。

另外,假设 dest 的长度为 1024, 而待拷贝的字符串长度只有 24,strncpy 会把余下的 1000 各字节全部置为 0. 这就可能会导致性能问题,这也是我为什么说 strncpy 是低效的。

误解二:如果src 长度大于等于 n, 那么 strncpy 会拷贝 n – 1 各字符到 dest, 然后补 0?

错,大错特错,罚抄上面的 DESCRIPTION ,直到看到:

if there is no null byte among the first n bytes of src, the result will not be null-terminated.

这就可能导致了不安全的因素。

如果待拷贝字符串长度大于了 n, 那么 dest 是不会有结尾字符 0 的。假设这样一种情况:

char s[] = "hello world";
strncpy(s, "shit!", 5);
puts(s);

输出的结果是 “shit” 还是 “shit! world” ?

这种情况只是导致了输出结果错误,严重的,如果 dest n 字节后面一直没有 0,那么就会导致程序段错误。

strncpy 最开始引入标准库是用来处理结构体中固定长度的字符串,比如路径名,而这些字符串的用法不同于 C 中带结尾字 0 的字符串。所以 strncpy 的初衷并不是一个安全的 strcpy.

那么用那些函数来替代 strncpy?

1、使用 snprintf

snprintf(dest, n, src);

的效果和我们对一个安全的字符串拷贝函数的期望完全一致。

但是这个函数效率有点问题,并且特殊字符比如 %d 会转义。

2、自己实现一个高效并且安全的字符串拷贝函数 sstrncpy,开头的 s 代表 safe

/* safe strncpy */

char *sstrncpy(char *dest, const char *src, size_t n)
{
if (n == 0)
return dest; dest[0] = 0; return strncat(dest, src, n - 1);
}

使用 strncat 是因为很难实现一个性能能够达到库函数的字符串拷贝函数。

3、但是,上面两个函数都有一个问题:如果不能预知 src 的最大长度,那么 src 会被静默的截断。

如果是为了复制一个字符串,那么更好的做法是使用 strdup 函数

char* strdup (const char *s);

strdup 函数会调用 malloc 分配足够长度的内存并返回。

当然,你需要在你不使用的时候 free 它。

如果只是函数内部调用,也可以使用 strdupa 函数。

char* strdupa (const char *s);

strdupa 函数调用 alloca函数而非 malloc 函数分配内存,alloca 分配的内存是桟内存而非堆内存。所以当函数返回后,内存就自动释放了,不需要 free。

4、如果是从文本文件中读数据,相对与 fgets 函数,更好的做法是使用 getline

ssize_t getline (char **lineptr,size_t *n,FILE *stream);

一个简单的例子:

char *line = NULL;
size_t len = 0; while (getline(&line, &len, stdin) != -1)
{
fputs(line, stdout);
} free(line);

当 line 为 NULL 或者 len 为 0 时,getline 调用 malloc 分配足够大的内存。所以你需要在用完后 free 它们。

和 fgets 相同,getline 得到的行是带换行字符的。

所以,忘了 strncpy 吧,血的教训,说出来都是泪…

[置顶] strcpy()与strncpy()的区别的更多相关文章

  1. [置顶] strcpy和memcpy的区别

    strcpy和memcpy都是标准C库函数,它们有下面的特点. strcpy提供了字符串的复制.即strcpy只用于字符串复制,并且它不仅复制字符串内容,还会复制字符串的结束符. 已知strcpy函数 ...

  2. [置顶] String StringBuffer StringBuilder的区别剖析

    这是一道很常见的面试题目,至少我遇到过String/StringBuffer/StringBuilder的区别:String是不可变的对象(final)类型,每一次对String对象的更改均是生成一个 ...

  3. [置顶] Array ArrayList LinkList的区别剖析

    这是一个面试中我们经常被问到的问题 Array.ArrayList.LinkList之间的区别:Array.ArrayList.LinkList均属于泛型的范畴,都用来存放元素,主要区别是Array是 ...

  4. [置顶] TIM_GetCounter与TIM_GetCapture1的区别

    /** * @brief Gets the TIMx Input Capture 1 value. * @param TIMx: where x can be 1 to 17 except 6 and ...

  5. strcpy、strncpy与memcpy的区别与使用方法

    strcpy.strncpy.memcpy这三个C语言函数我们在主机代码编写中会很频繁的使用到,但是三个函数的区别.使用时该注意什么还是有必要说下的. 本文参考<C 标准库>编写. 一.函 ...

  6. strcpy、strncpy、memcpy的区别

    一.strcpy.strncpy区别 struct gpInfo { char gpcode[9]; char gpName[50]; }; string gpstr = "SZ000001 ...

  7. 自定义置顶TOP按钮

    简述一下,分为三个步骤: 1. 添加Html代码 2. 调整Css样式 3. 添加Jquery代码 具体代码如下: <style type="text/css"> #G ...

  8. [知了堂学习笔记]_css3特效第二篇--行走的线条&&置顶导航栏

    一.行走的线条. 效果图(加载可能会慢一点儿,请稍等...): html代码: <div class="movingLines"> <img src=" ...

  9. 变量声明置顶规则、函数声明及函数表达式和函数的arguments属性初始化

    一.变量声明和变量赋值: if (!("a" in window)) { ; } alert(a);//a为? 你可能认为alert出来的结果是1,然后实际结果是“undefine ...

随机推荐

  1. QT QFtp使用实例 从FTP下载一个文件

    1. ftp://ftp.denx.de/pub/u-boot/lowboot-1.0.0.patch.gz  下载文件 FtpGet.h #ifndef FTPGET_H #define FTPGE ...

  2. 免配置环境变量使用Tomcat+设置项目主页路径为http://localhost:8080+修改tomcat端口号

    一.免配置jdk JAVA_HOME和tomcat  CATALINA_HOME环境变量使用tomcat 众说周知,使用tomcat需要有java环境,一般情况下需要配置jdk和tomcat的路径到w ...

  3. 51nod 1009 数字1的数量 数位dp

    1009 数字1的数量 基准时间限制:1 秒 空间限制:131072 KB   给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数.   例如:n = 12,包含了5个1 ...

  4. JMeter ——Test fragment

    fragment 为片段,可以是一个不完整的用例.比如你可以把一个http请求保存为fragment,如果不这样做的话,你是必须先要添加一个测试计划-线程组-http请求的.你可以把某步骤一系列的请求 ...

  5. Mirantis对OpenStack的性能测试:高并发创建75000台虚拟机

    硅谷创业公司Mirantis不久前进行了一项基准测试,测试在OpenStack Havana版本上创建75000台虚拟机的性能数据.就启动时间和成功率而言,当应用250个并发部署75000台虚拟机是最 ...

  6. nova libvirt event

    nova中利用libvirt 事件来更新vm的DB中的power状态 https://wiki.openstack.org/wiki/ComputeDriverEvents Nova compute ...

  7. HTML5 新元素之VIDEO标签的js操作

    本文参考w3school的HTML DOM Video 对象. Video 对象属性 属性 描述 audioTracks 返回表示可用音频轨道的 AudioTrackList 对象. autoplay ...

  8. 新东方雅思词汇---7.2、warrant

    新东方雅思词汇---7.2.warrant 一.总结 一句话总结: warr+ant 英 ['wɒr(ə)nt]  美 ['wɔrənt]  n. 根据:证明:正当理由:委任状 vt. 保证:担保:批 ...

  9. mysql数据库优化课程---4、mysql字段属性

    mysql数据库优化课程---4.mysql字段属性 一.总结 一句话总结: mysql字段:没有被淘汰的必然有其存在的合理性(也就是有优点),比如char类型 1.为什么char类型没有被淘汰? c ...

  10. java: BufferedReader简单的菜单

    BufferedReader简单的菜单 Menu boolean flag; public Menu() { while(true) { this.show(); } } public void sh ...