一、函数说明
1、strcpy函数
char* strcpy(char* dest, const char *src);
说明:
函数strcpy把src指向的串(包括空字符)复制到dest指向的数组中。如果复制发生在两个重叠的对象中,则这种行为未定义,其中如果中间字符遇到'\0'停止复制。
返回值:
函数strcpy返回dest的值。
举例:
#include<stdio.h>
#include<string.h>
char str[4];
int main()
{
strcpy(str,"abc");
printf("%s\n",str);
return 0;
}
结果:
[wanghe@localhost ~]$ gcc test_string.c -o test_string.exe
[wanghe@localhost ~]$ ./test_string.exe
abc
2、strncpy函数
char* strncpy(char* dest, const char* src, size_t count);
说明:
函数strncpy从src指向的数组中最多复制count个字符(不复制空字符后面的字符)到dest指向的数组中。如果复制发生在两个重叠的对象中,则这种行为未定义,其中如果中间字符遇到'\0'停止复制。
如果src指向的数组是一个比n短的字符串,则在dest定义的数组后面补空字符,直到写入了count个字符。
返回值:
函数strncpy返回dest的值。
举例(注意复制长度是2):
#include<stdio.h>
#include<string.h>
char str[4];
int main()
{
strncpy(str,"abc",2);
printf("%s\n",str);
return 0;
}
结果:
[wanghe@localhost ~]$ gcc test_string.c -o test_string.exe
[wanghe@localhost ~]$ ./test_string.exe
ab
3、memcpy函数
void* memcpy(void *dest, const void *src, size_t count);
说明:
函数memcpy从src指向的对象中复制count个字符到dest指向的对象中。如果复制发生在两个重叠的对象中,则这种行为未定义。
返回值:
函数memcpy返回dest的值。
举例:
#include<stdio.h>
#include<string.h>
char str[4];
int main()
{
char tmp[] = "abc";
memcpy(str,tmp,3);
printf("%s\n",str);
return 0;
}
结果:
[wanghe@localhost ~]$ gcc test_string.c -o test_string.exe
[wanghe@localhost ~]$ ./test_string.exe
abc
4、memmove函数
void* memmove(void* dest, const void* src, size_t count)
说明:
memmove函数的功能同memcpy基本一致,但是当src区域和dest内存区域重叠时,memcpy可能会出现错误,而memmove能正确进行拷贝。
返回值:
函数memmove返回dest的值。
举例(长度是2):
#include<stdio.h>
#include<string.h>
char str[4];
int main()
{
char tmp[] = "abc";
memmove(str,tmp,2);
printf("%s\n",str);
return 0;
}
结果:
[wanghe@localhost ~]$ gcc test_string.c -o test_string.exe
[wanghe@localhost ~]$ ./test_string.exe
ab
二、函数对比与问题
1.strcpy与strncpy
strcpy函数是复制字符串,遇到'\0'结束复制,这其中的问题是如果目标空间长度小于源字符串长度,会导致原本的内存数据被覆盖。
举例:
#include<stdio.h>
#include<string.h>
int main()
{
char d[5] = {0};
char s[] = "hello world";
strcpy(d,s);
return 0;
}
也许运行结果可能觉得没有问题,但实际上会覆盖原本不属于d的内存,导致内存。所以可以采用strncpy函数,strncpy函数是复制n个字符到目标地址。
比如上述程序可以改写成:
#include<stdio.h>
#include<string.h>
int main()
{
char d[5] = {0};
char s[] = "hello world";
strncpy(d,s,sizeof(d)-1);
printf("%s\n",d);
return 0;
}
虽然没有复制完全,但是不会出现内存问题。为什么是sizeof(d)-1呢?主要是由于strncpy是不会复制'\0'的,这一点要特别注意!
但是strncpy函数也有它的一些问题:
(1)内存越界问题,如果复制的字符数量n > 目标空间长度,就会越界
(2)字符串结束标志符'\0'丢失,当源字符串的长度 == 目标空间大小时,调用strncpy使得目标字符串结束标志符'\0'丢失。
举例:
#include<stdio.h>
#include<string.h>
int main()
{
char d[20] = "abcd efg";
char s[] = "he";
strncpy(d,s,sizeof(s)-1);
printf("%s\n",d);
return 0;
}
结果:
[wanghe@localhost ~]$ gcc test_string.c -o test_string.exe
[wanghe@localhost ~]$ ./test_string.exe
hecd efg
从结果上可以看出,复制结束后是不会在尾部加'\0'的,这意味着如果复制字符串的长度和目标空间大小相等,那么存储的'\0'就会被覆盖,这会导致错误!
(3)效率较低,当 n > 源字符串长度时,会继续填充'\0'直到count长度为止。
举例:
#include<stdio.h>
#include<string.h>
int main()
{
char d[20] = "abcd efg";
char s[] = "he";
strncpy(d,s,4);
int i = 0;
for(i = 0;i < 8;i++)
printf("%c\n",d[i]);
return 0;
}
执行结果:
[wanghe@localhost ~]$ gcc test_string.c -o test_string.exe
[wanghe@localhost ~]$ ./test_string.exe
h
e
e
f
g
(4)不能处理源字符串和目标地址内存交叠情况的字符串拷贝,这一点需要使用memmove函数。
2.strcpy与memcpy
strcpy函数和strncpy函数在复制字符串的时候遇到 '\0'就会结束,因为函数默认'\0'到达了字符串尾部,但如果要复制的字符串中间有0,那么就会没有复制完全就结束了,这显然不符合预期。
举例:
#include<stdio.h>
#include<string.h>
int main()
{
char d[20] = "ab\0cd efg";
char s[12] = {0};
strcpy(s,d);
int i = 0;
for(i = 0;i < 12;i++)
printf("%d ",s[i]);
return 0;
}
结果:
[wanghe@localhost ~]$ gcc test_string.c -o test_string.exe
[wanghe@localhost ~]$ ./test_string.exe
97 98 0 0 0 0 0 0 0 0 0 0
所以如果字符串中有'\0'就不能用strcpy和strncpy函数了,必须采用内存复制函数,如memcpy函数。
举例:
#include<stdio.h>
#include<string.h>
int main()
{
char d[20] = "ab\0cd efg";
char s[12] = {0};
memcpy(s,d,sizeof(d)-1 );
int i = 0;
for(i = 0;i < 12;i++)
printf("%d ",s[i]);
return 0;
}
结果:
[wanghe@localhost ~]$ gcc test_string.c -o test_string.exe
[wanghe@localhost ~]$ ./test_string.exe
97 98 0 99 100 32 101 102 103 0 0 0
因为有0,所以直接输出字符串是看不到具体效果的,所以输出ASCII值。
3.memcpy与memmove
前面提到了无论是strncpy还是memcpy函数,都有一个缺陷那就是如果源地址和目标地址内存上有重叠,在C语言中是未定义行为,结果可能会出现问题,所以为了避免这一情况,采用memmove函数。
首先如果源地址在目标地址后面,那么不会有什么问题。
如果是在前面,那么复制的时候会改变本身的值,原则上应该是倒序复制,这样就可以避免,但是memcoy函数是从左到右顺序复制的,所以无法处理内存重叠问题。
memmove函数是在执行的时候会去判断源地址和目标地址是否重叠,如果重叠会倒序复制顺序使得不会冲突。