在写C程序的时候,经常会用到串拷贝strcp这个库函数,它的主要功能就是将一串字符串常量拷贝到一个字符串变量里(而不是一个字符串常量,必须要有存储空间)。虽然这个函数的实现比较简单,但是最基本的东西往往容易被人忽视。
c99标准参考 :
7.21.2.3 The strcpy function Synopsis 1 #include <string.h> char *strcpy(char * restrict s1, const char * restrict s2); Description 2 The strcpy function copies the string pointed to by s2 (including the terminating null character) into the array pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined. Returns 3 The strcpy function returns the value of s1. restrict只是在语言层次上保证指针指向的是不同对象。 就像inline是建议内联,const是语言上保证的const性。 const int x = 1; 语言层次上x的值不可修改。 在实现上可以强制修改,但不能保证了const变量的立即数优化的结果。
使用库函数中strcpy的方法如下:
/*函数名: strcpy *功 能: 串拷贝 *用 法: char *strcpy(char *str1, char *str2); *程序例: */ #include <stdio.h> #include <string.h> //包含库函数strcpy的头文件 int main(void) { char string[10]; //申请一块空间 char *str1 = "abcdefghi"; //字符串常量 strcpy(string, str1); //调用库函数strcpy printf("%s/n", string); return 0; } //输出应该是:abcdefghi
自己定义实现strcpy库函数的方法如下:
/************************************************************************/ /* author: wcdj * date: 2009-10-6 * description: to define strcpy function * language: c */ /************************************************************************/ #include <stdio.h> #include <assert.h> char* strcpy(char *pSrc, const char *pNew) { assert(pSrc!=NULL && pNew!=NULL);//check //copy value while (*pNew) { *pSrc=*pNew; pNew++; pSrc++; } *pSrc='/0';//end /*code to copy value can be replaced by code like following*/ // while((*pSrc++=*pNew++)!='/0') // { // NULL; // } return pSrc; } int main() { char strSrc[]="wcdj is a guy"; printf("the strSrc is: %s/n",strSrc); const char* strNew="Hi wcdj"; printf("the strNew is: %s/n",strNew); strcpy(strSrc,strNew); printf("after strcpy,the strSrc is: %s/n",strSrc); return 0; } /* 输出为: the strSrc is: wcdj is a guy the strNew is: Hi wcdj after strcpy,the strSrc is: Hi wcdj Press any key to continue */
经网友指出,上述自定义strcpy库函数的方法存在问题,修正如下:
/**********************************************/ /* Author: wcdj * Modification Date: 2010-3-9 */ /**********************************************/ #include <stdio.h> #include <assert.h> // [1] // 有返回值的情形 char* strcpy(char *pSrc, const char *pNew) { assert(pSrc!=NULL && pNew!=NULL);// check // strcpy有返回值的时候,保存pSrc的起始位置值 char *pTemp=pSrc; // copy value while (*pNew) { *pSrc=*pNew; pNew++; pSrc++; } *pSrc='/0';// end /*code to copy value can be replaced by code like following*/ // while((*pSrc++=*pNew++)!='/0') // { // NULL; // } //return pSrc;// error return pTemp;// ok } int main() { char strSrc[]="wcdj is a guy"; printf("the strSrc is: %s/n",strSrc); const char* strNew="Hi wcdj"; printf("the strNew is: %s/n",strNew); strcpy(strSrc,strNew); printf("after strcpy,the strSrc is: %s/n",strSrc); printf("after strcpy,the strSrc is: %s/n",strcpy(strSrc,strNew)); return 0; } /* 输出为: the strSrc is: wcdj is a guy the strNew is: Hi wcdj after strcpy,the strSrc is: Hi wcdj after strcpy,the strSrc is: Hi wcdj Press any key to continue */ // [2] // 无返回值的情形 void strcpy(char *pSrc, const char *pNew) { assert(pSrc!=NULL && pNew!=NULL);// check // copy value while (*pNew) { *pSrc=*pNew; pNew++; pSrc++; } *pSrc='/0';// end /*code to copy value can be replaced by code like following*/ // while((*pSrc++=*pNew++)!='/0') // { // NULL; // } return; } int main() { char strSrc[]="wcdj is a guy"; printf("the strSrc is: %s/n",strSrc); const char* strNew="Hi wcdj"; printf("the strNew is: %s/n",strNew); strcpy(strSrc,strNew); printf("after strcpy,the strSrc is: %s/n",strSrc); return 0; } /* 输出为: the strSrc is: wcdj is a guy the strNew is: Hi wcdj after strcpy,the strSrc is: Hi wcdj Press any key to continue */
网友的方法:
// 最简单的写法 char *strcpy(char *dest, const char *src) { char *save = dest; while(*dest++ = *src++);//加上!='0',纯粹是脱裤子放屁--多此一举。 return save; } // 稍复杂点儿的写法 char *strcpy(char *dest, const char *src) { assert( dest ); assert( src ); char *save = dest; while(*dest++ = *src++); return save; } //同等效率也可以这么写 char *strcpy(char *dest, const char *src) { char *save = dest; for (;;) { *dest = *src; if (*src == '/0') break; ++dest, ++src; } return save; } //如果对安全要求比较高,可以使用strlcpy size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '/0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '/0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } /*另外,程序里边不要随便用null/NULL,并不是所有的编译器都支持的 如果你非要用不可,记得用之前加上这么几句:*/ #ifndef NULL #define NULL 0 #endif
关于此函数用法的一个的帖子:
http://topic.csdn.net/u/20090901/08/1a5757e5-3467-4c27-884b-dd7401888dd4.html
[参考资料]
strcpy,strncpy,strlcpy,memcpy使用的介绍:
来源:http://www.cnblogs.com/buffer/archive/2009/06/01/1493781.html
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); 3. strlcpy // Copy src to string dst of size siz. At most siz-1 characters // will be copied. Always NUL terminates (unless siz == 0). // Returns strlen(src); if retval >= siz, truncation occurred. size_t strlcpy(char *dst, const char *src, size_t siz); 而使用 strlcpy,就不需要我们去手动负责 /0 了,仅需要把 sizeof(dst) 告之 strlcpy 即可: strlcpy(path, src, sizeof(path)); len = strlen(src); if ( len >= sizeof(path) ) printf("src is truncated."); 并且 strlcpy 传回的是 strlen(str),因此我们也很方便的可以判断数据是否被截断。 4 memcpy memcpy 原型:extern void *memcpy(void *dest, void *src, unsigned int count); 用法:#include <string.h> 功能:由src所指内存区域复制count个字节到dest所指内存区域。 说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。 举例: // memcpy.c #include <syslib.h> #include <string.h> main() { char *s="Golden Global View"; char d[20]; clrscr(); memcpy(d,s,strlen(s)); d[strlen(s)]=0; printf("%s",d); getchar(); return 0; } [* 一点点历史 *] strlcpy 并不属于 ANSI C,至今也还不是标准。 strlcpy 来源于 OpenBSD 2.4,之后很多 unix-like 系统的 libc 中都加入了 strlcpy 函数,我个人在 FreeBSD、Linux 里面都找到了 strlcpy。(Linux使用的是 glibc,glibc里面有 strlcpy,则所有的 Linux 版本也都应该有 strlcpy) 但 Windows 下是没有 strlcpy 的,对应的是strcpy_s函数 说明: strcpy只是复制字符串,但不限制复制的数量。很容易造成缓冲溢出。 strncpy要安全一些。 memcpy也可以用来在内存中复制数据,由于字符串是以零结尾的,所以对于在数据中包含零的数据只能用memcpy。 性能上它们不一定有多大差别。
strlcpy and strlcat - consistent, safe, string copy and concatenation.
http://www.gratisoft.us/todd/papers/strlcpy.html
[2010-3-17 wcdj 修改此文]