strcpy和memcpy都是标准C库函数,它们有下面的特点。
strcpy提供了字符串的复制。即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符。
memcpy提供了一般内存的复制。即memcpy对于需要复制的内容没有限制,因此用途更广。
mem系列函数是面试的时候常考的知识点,我们需要熟练掌握这三个函数的原理和代码实现,要能准确无误的写出代码。
memcpy、memset和memset三个函数在使用过程中,均需包含以下头文件:
1
2
3
4
|
//在C中 #include<string.h> //在C++中 #include<cstring> |
memcpy
memcpy函数是C/C++中的内存拷贝函数,它的功能是从源src所指的内存地址的起始位置开始,拷贝n个字节到目标dst所指的内存地址的起始位置中。
研究函数功能最好的办法就是研究其源代码,这里在网上找了一份,如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
void *__cdeclmemcpy( void * dst,constvoid* src,size_tcount) { void * ret = dst; while (count--) { // 注意, memcpy函数没有处理dst和src区域是否重叠的问题 *( char *)dst = *( char *)src; dst = ( char *)dst +1; src = ( char *)src +1; } return (ret); } |
源代码比较简单,定义一个计数,然后从头到尾一次将src指向的值拷贝给dst,库函数中的memcpy不能处理dst和src中存在重叠部分这种情况。
那么处理重叠部分的话,我们可以采用从后往前依次拷贝的方法,下面给出我修改过的函数代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
void *__cdeclmemcpy( void * dst,constvoid* src,size_tcount) { char *pDst = static_cast < char *> dst; constchar*pSrc = static_cast <constchar*> src; //检查参数 if (pDst==NULL|| pSrc==NULL|| count <=0){ returnNULL; } //判断有是否存在重叠部分 if (pDst > pSrc && pDst < pSrc + count){ for (size_ti=count-1; i>=0; i--) { pDest[i] = pSrc[i]; } } else { for (size_ti=0; i<count; i++) { pDest[i] = pSrc[i]; } } returnpDst; } |
memset
memset一般用于对内存初始化,在这里需要注意的是,memset函数是对内存的每个字节(按字节)设置成c的值。其函数原型如下:
1
2
3
4
5
6
7
8
|
voidmemset( void *s,intc,size_tn) { constunsignedcharuc = c; //将int转换成char,截去c的高24位,留下低8位 unsignedchar*su; for (su = s;0< n; ++su, --n) *su = uc; returns; } |
注意,这里有一个坑,memset一般用于将内存清零,你要是想将这段内存初始化为1而写下下面的代码:
1
2
|
intnum[10]; memset (num,1, sizeof ( int )*10); |
这里并不会如你所愿,num的每一个数都被初始化为16843009,原因就是上述提到的会截去c的高24位。
使用memset初始化比用for循环初始化要快很多,所以在初始化基本类型数据,结构体等的时候尽量选择memset,memset可以方便的清空一个结构类型的变量或数组。
memmove
它与memcpy的功能相似,都是将src所指的n个字节复制到dst所指的内存地址的起始位置,不同的是它处理了src和dst有重叠的情况。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。(与上述修改过得memcpy基本一致)
所以基本原则就是,如果你能确保两段内存没有重叠的部分,就使用memcpy来进行拷贝;如果你不能确定,为了保证复制的正确性,必须用memmove。
其实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
void * memmove ( void * dest, void * src,size_tcount) { void * ret = dest; if (dest <= src || dest >= (src + count)) { //Non-Overlapping Buffers //copy from lower addresses to higher addresses while (count --) *dest++ = *src++; } else { //Overlapping Buffers //copy from higher addresses to lower addresses dest += count - 1; src += count - 1; while (count--) *dest-- = *src--; } returnret; } |
strcpy
strcpy是C语言的标准库函数,使用strcpy需要包含以下头文件:
1
2
|
#include<string.h> #include<stdio.h> |
其函数功能是把从src地址开始且含有NULL结束符的字符串复制到dst开始的地址空间,返回指向dst的指针。其函数代码如下:
1
2
3
4
5
6
7
|
char * strcpy ( char * dst , char * src){ if (dst==NULL||src==NULL)returnNULL; // --1 if (dst==src)returndst; //--2 char * address = dst; //--3 while ((*dst++ = *src++)!= '\0' ) //--4 returnaddress; //--5 } |
图中标出来的都是考点,下面一一说明:
1、需要判断参数的正确性,这里也可以抛出一个异常
2、如果指向了同一块内存,不用复制直接返回即可
3、这里需要保存原始的dst指针,用作返回值
4、这里有一个技巧,如果写成以下两种,面试的时候会大大扣分!
1
2
3
4
|
//第一种 while (*dst++ = *src++) //直接越界访问,没有检查指针的有效性 //第二种 while (*src!= '\0' ){*dst++ = *src++;} //考虑了src的边界问题,没有在dst的后面加'\0',会导致dst的长度未知引起错误 |
5、函数返回dst的原始值是为了能够支持链式表达式,增加了函数的附加性。
上述第5点可以用如下测试代码来说明:
intlength =strlen(strcpy(strA,strB));//如果不支持链式表达式,这里会报错。
那么有时候也会问为什么不返回src的原始值,错误原因有以下三点:
源字符串本来就已知,返回没有什么意义
不能支持形如char * strA = strcpy(new char[10],strB) 这样的表达式
为了保护源字符串,使用const限定了src所指的内容,把const char作为char 的返回值,类型不符,编译器会报错。
strcpy和memcpy主要有以下3方面的区别。
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
以上所述是小编给大家介绍的C/C++中的mem函数和strcopy函数的区别和应用,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!