天天看点

strcpy、strncpy、memcpy、memmovestrcpystrncpymemcpymemmovestrlen注意事项字符数组中间有结束符’\0’拷贝情况内存区域重叠拷贝strcpy和strncpy比较strncpy和memcpy比较memcpy和memmove比较参考资料

文章目录

  • 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’终止字符停止)

strcpy、strncpy、memcpy、memmovestrcpystrncpymemcpymemmovestrlen注意事项字符数组中间有结束符’\0’拷贝情况内存区域重叠拷贝strcpy和strncpy比较strncpy和memcpy比较memcpy和memmove比较参考资料

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