文章目录
- strcpy
-
- 描述和声明
- 参数要求
- 实例
- strncpy
-
- 描述和声明
- 参数要求
- 实例
- memcpy
-
- 描述和声明
- 参数要求
- 实例
- memmove
-
- 描述和声明
- strlen注意事项
- 字符数组中间有结束符'\0'拷贝情况
- 内存区域重叠拷贝
- strcpy和strncpy比较
- strncpy和memcpy比较
- memcpy和memmove比较
- 参考资料
strcpy
描述和声明
描述
C 库函数 char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。
需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。
声明
#include <string.h>
char *strcpy(char *dest, const char *src);
参数要求
1.参数dest和src为非空参数,src字符要有结束符’\0’;
2.dest空间要大于等于src字符长度;
3.src尽量保证有终止null字节(’\0’),否则strlen判断长度会不准;
4.dest和src字符串不能重叠,否则会覆盖原数据;
实例
实例1:dest内存空间 < src内存空间
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[]="Sample string";//
char str2[8] = {0};
//打印str1和str2内存空间大小
printf ("sizeof(str1):%u, sizeof(str2):%u\n",sizeof(str1),sizeof(str2));
//打印str1和str2内存空间地址,一般str1和str2是连续的内存地址
printf ("str1_begin:%p,str1_end:%p\n",&str1,&str1[strlen(str1)]);
printf ("str2_begin:%p str2_end:%p\n",&str2,&str2[7]);
//打印str1和str2内容
printf (" str1:%s\n str2:%s\n",str1,str2);
//strcpy拷贝,拷贝14个字节到str2
strcpy(str2,str1);
//打印str1和str2内容
printf (" str1:%s\n str2:%s\n",str1,str2);
return 0;
}
输出如下:
sizeof(str1):14, sizeof(str2):8
str1_begin:0x7ffe568115d2,str1_end:0x7ffe568115df
str2_begin:0x7ffe568115ca str2_end:0x7ffe568115d1
str1:Sample string
str2:
str1:tring
str2:Sample string
结果:1. str1内容被覆盖修改了;2.str2输出超出了8个字节长度;原因如下图:(注:printf输出是遇到’\0’终止字符停止)
strncpy
描述和声明
描述
C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
声明
#include <string.h>
char *strncpy(char *dest, const char *src, size_t n);
参数要求
1.参数dest和src为非空参数,src字符要有结束符’\0’;
2.dest空间要大于等于src字符长度;
3.src尽量保证有终止null字节(’\0’),否则strlen判断长度会不准;
4.dest和src字符串不能重叠,否则会覆盖原数据;
实例
包括src空间大于dest1、小于dest2、等于dest3的情况,注:src包含结束符’\0’;
#include <stdio.h>
#include <string.h>
int main()
{
char src[12] = "hello world";
char dest1[8];
char dest2[32];
char dest3[12];
memset(dest1, '\0', sizeof(dest1));
memset(dest3, '\0', sizeof(dest3));
memset(dest2, 'a', sizeof(dest2));
dest2[32-1] = '\0'; //保证dest2有结束符,避免printf输出不准
printf("[dest1:%s]\n[dest2:%s]\n", dest1,dest2);
//dest2空间打印src空间、不拷贝'\n'结束符
strncpy(dest2, src, strlen(src)-1);
printf("[dest2:%s]\n", dest2);
//dest3空间和src空间相等,src有结束符
strncpy(dest3, src, strlen(src));
printf("[dest3:%s]\n", dest3);
//dest1空间小于src空间,安全拷贝
memset(dest1, '\0', sizeof(dest1));
strncpy(dest1, src, sizeof(dest1)-1);//sizeof(dest1)必须减1保留'\n'结束符
printf("[dest1:%s]\n", dest1);
//dest1空间小于src空间,非安全拷贝
memset(dest1, '\0', sizeof(dest1));
strncpy(dest1, src, strlen(src));
//或
//strncpy(dest1, src, sizeof(dest1));
printf("[dest1:%s]\n", dest1);//已越界输出:[dest1:hello worldlo world]
return(0);
}
输出:
[dest1:]
[dest2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
[dest2:hello worlaaaaaaaaaaaaaaaaaaaaa]
[dest3:hello world]
[dest1:hello w]
[dest1:hello worldlo world]
推荐如下方式使用strncpy
#include <stdio.h>
#include <string.h>
int main()
{
char src[12] = "hello world";
char dest1[8];
char dest2[32];
memset(dest1, '\0', sizeof(dest1));
memset(dest2, '\0', sizeof(dest2));
strncpy(dest1, src, sizeof(dest1)-1);//sizeof(dest)必须减1保留'\n'结束符
strncpy(dest2, src, sizeof(dest2)-1);//sizeof(dest)必须减1保留'\n'结束符
printf("[dest1:%s]\n[dest2:%s]\n", dest1,dest2);
return(0);
}
输出:
[dest1:hello w]
[dest2:hello world]
注意点:
1.做字符数组的初始化;
2.使用sizeof()-1保证字符数数组有结束符,避免src长度大于dest长度越界拷贝;
3.若程序工程中结构定义都使用宏、且字符数据都没有结束符的话,不能用sizeof()-1的方式;
memcpy
描述和声明
描述
C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字符到存储区 str1。
声明
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
参数要求
1.参数str1和str2为非空参数,str2字符要有结束符’\0’;
2.str2空间要大于等于str1字符长度;
3.str2尽量保证有终止null字节(’\0’),否则strlen判断长度会不准;
4.dest和src字符串不能重叠,否则会覆盖原数据;
实例
#include <stdio.h>
#include <string.h>
int main ()
{
const char src[12] = "hello world";
char dest1[12];
memset(dest1,0,sizeof(dest1));
memcpy(dest1, src, strlen(src));//拷贝了11个字节
printf("dest1 = %s\n", dest1);
//内存重叠拷贝
memcpy(dest1, &dest1[7], strlen(&dest1[7])+1);//strlen长度计算不包括'\0'结束符
printf("dest1 = %s\n", dest1);
return(0);
}
输出:
dest1 = hello world
dest1 = orld
memmove
描述和声明
描述
C 库函数 void *memmove(void *str1, const void *str2, size_t n) 从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。
声明
#include <string.h>
void *memmove(void *dest, const void *src, size_t n);
strlen注意事项
注:strlen长度计算不包括’\0’结束符。
字符数组中间有结束符’\0’拷贝情况
先初始化str,之后将第20个字节赋值为结束符’\0’,查看strcpy、strncpy、memcpy、memmove的拷贝情况。
// 将字符串复制到数组 dest 中
#include <stdio.h>
#include <string.h>
int main ()
{
char src[32] = {'a','b','c','d','e','f','g','h','i','g','k','l','m','n','o','p','q',\
'r','s','t','u','v','w','x','y','z','1','2','3','4','5'};
char dest1[32];
char dest2[32];
char dest3[32];
char dest4[32];
printf("src = %s\n", src);
src[19] = '\0'; //src中间添加一个结束符
printf("src = %s\n", src);
memset(dest1,0,sizeof(dest1));
strcpy(dest1, src);
printf("[strcpy][dest1:%s] [&dest1[20]:%s]\n", dest1,&dest1[20]);
memset(dest2,0,sizeof(dest2));
strncpy(dest2, src, 32);
printf("[strncpy][dest2:%s] [&dest2[20]:%s]\n", dest2,&dest2[20]);
memset(dest3,0,sizeof(dest3));
memcpy(dest3, src, 32);
printf("[memcpy][dest3:%s [&dest3[20]:%s]\n", dest3,&dest3[20]);
memset(dest4,0,sizeof(dest4));
memmove(dest4, src, 32);
printf("[memmove][dest4:%s [&dest4[20]:%s]\n", dest4,&dest4[20]);
return(0);
}
输出
src = abcdefghigklmnopqrstuvwxyz12345
src = abcdefghigklmnopqrs
[strcpy][dest1:abcdefghigklmnopqrs] [&dest1[20]:]
[strncpy][dest2:abcdefghigklmnopqrs] [&dest2[20]:]
[memcpy][dest3:abcdefghigklmnopqrs [&dest3[20]:uvwxyz12345]
[memmove][dest4:abcdefghigklmnopqrs [&dest4[20]:uvwxyz12345]
结论:
1.strcpy、strncpy遇到结束符’\0’时拷贝终止;
2.memcpy、memmove是内存拷贝,不管内存内容;
内存区域重叠拷贝
当src和dest内存出现重叠时,strcpy、strncpy、memcpy、memmove的拷贝情况:
#include <stdio.h>
#include <string.h>
int main ()
{
char src1[8] = {'1','2','3','4','5','6','7'};
char src2[8] = {'1','2','3','4','5','6','7'};
char src3[8] = {'1','2','3','4','5','6','7'};
char src4[8] = {'1','2','3','4','5','6','7'};
char *dest1 = &src1[2]; //执行src第三个字节'3'
char *dest2 = &src2[2]; //执行src第三个字节'3'
char *dest3 = &src3[2]; //执行src第三个字节'3'
char *dest4 = &src4[2]; //执行src第三个字节'3'
printf("[src1:%s] [dest1:%s]\n", src1,dest1);
printf("[src2:%s] [dest2:%s]\n", src2,dest2);
printf("[src3:%s] [dest3:%s]\n", src3,dest3);
printf("[src4:%s] [dest4:%s]\n\n", src4,dest4);
strncpy(dest2, src2, sizeof(src2)-3);//拷贝5个字符,保留末尾结束符,要计算这个长度
printf("[strncpy][src2:%s][dest2:%s]\n", src2,dest2);
memcpy(dest3, src3, sizeof(src3)-3);//拷贝5个字符,保留末尾结束符,要计算这个长度
printf("[memcpy][src3:%s][dest3:%s]\n", src3,dest3);
memmove(dest4, src4, sizeof(src4)-3);//拷贝5个字符,保留末尾结束符,要计算这个长度
printf("[memmove][src4:%s][dest4:%s]\n", src4,dest4);
//strcpy放在下面,放在上面影响操作结果
strcpy(dest1, src1);//拷贝7个字符,会拷贝越界,printf输出超7个字符
printf("[strcpy][src1:%s] [dest1:%s]\n", src1, dest1);
return(0);
}
输出:
[src1:1234567] [dest1:34567]
[src2:1234567] [dest2:34567]
[src3:1234567] [dest3:34567]
[src4:1234567] [dest4:34567]
[strncpy][src2:1212345][dest2:12345]
[memcpy][src3:1212123][dest3:12123]
[memmove][src4:1212345][dest4:12345]
[strcpy][src1:121234567] [dest1:1234567]
结论(内存重叠时):
1.strcpy和strncpy是字符副本拷贝;
2.memcpy是纯内存单字节拷贝,内存重叠时,若已覆盖src内存,以覆盖后的结果拷贝;
3.memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。
strcpy和strncpy比较
strcpy()函数将src指向的字符串(包括终止null字节(’\0’))复制到缓冲区由dest指向的。字符串不能重叠,目标字符串dest必须足够大以接收副本。
strncpy()函数与此类似,只是最多复制了n个字节的src。警告:如果没有空字节在src的前n个字节中,放置在dest中的字符串不会以null结尾。
如果src的长度小于n, strncpy()用空字节填充dest的其余部分。
结果:
strcpy()拷贝直到遇到第一个终止字符’\0’,拷贝才终止,dest拷贝结果包括了终止字符;
strncpy()最多拷贝n个字节:
若src的长度小于n,strncpy()用空字节填充dest的其余部分;
若src的长度大于n,则拷贝n个字节,拷贝的n个字节中无终止字符,要memset或赋值’\0’保证终止字符的存在;
strncpy和memcpy比较
C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字符到存储区 str1。
结果:
1.strncpy遇到’\0’会提前终止拷贝,并把之后的字节设置为空字符;
2.memcpy是纯字节拷贝,不管内容;
memcpy和memmove比较
C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字符到存储区 str1。
C 库函数 void *memmove(void *str1, const void *str2, size_t n) 从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。
结果–内存重叠时:
1.memcpy一个字节一个字节拷贝,内存重叠拷贝过程中,src源数据变化,以变化后的结果拷贝;
2.memmove在内存重叠时,能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中;
实例:
参考资料
https://blog.csdn.net/wojiuguowei/article/details/79553813
https://www.runoob.com/cprogramming/c-standard-library-string-h.html