博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C/C++的mem函数和strcpy函数的区别和应用
阅读量:3612 次
发布时间:2019-05-21

本文共 8615 字,大约阅读时间需要 28 分钟。

mem系列函数是面试的时候常考的知识点,我们需要熟练掌握这三个函数的原理和代码实现,要能准确无误的写出代码。

memcpy、memset和memset三个函数在使用过程中,均需包含以下头文件:

1     
2
3
4
//在C中     
#include
//在C++中
#include

memcpy

memcpy函数是C/C++中的内存拷贝函数,它的功能是从源src所指的内存地址的起始位置开始,拷贝n个字节到目标dst所指的内存地址的起始位置中。

研究函数功能最好的办法就是研究其源代码,这里在网上找了一份,如下:

1     
2
3
4
5
6
7
8
9
10
11
12
void *       
__
cdecl memcpy ( void * dst,const void * src,size_t count)
{
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 *       
__
cdecl memcpy ( void * dst,const void * src,size_t count)
{
char *pDst =
static_cast<
char *> dst;
const
char *pSrc =
static_cast<
const
char *> src;
//检查参数
if(pDst==
NULL || pSrc==
NULL || count <=
0){
return
NULL;
}
//判断有是否存在重叠部分
if(pDst > pSrc && pDst < pSrc + count){
for(
size_t i=count
-1; i>=
0; i--)
{
pDest[i] = pSrc[i];
}
}
else {
for(
size_t i=
0; i

memset

memset一般用于对内存初始化,在这里需要注意的是,memset函数是对内存的每个字节(按字节)设置成c的值。其函数原型如下:

1     
2
3
4
5
6
7
8
void memset(void *s, int c, size_t n)     
{
const
unsigned
char uc = c;
//将int转换成char,截去c的高24位,留下低8位
unsigned
char *su;
for (su = s;
0 < n; ++su, --n)
*su = uc;
return s;
}

注意,这里有一个坑,memset一般用于将内存清零,你要是想将这段内存初始化为1而写下下面的代码:

1     
2
int num[      
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_t count)     
{
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--;
}
return ret;
}

strcpy

strcpy是C语言的标准库函数,使用strcpy需要包含以下头文件:

1     
2
#include 
#include

其函数功能是把从src地址开始且含有NULL结束符的字符串复制到dst开始的地址空间,返回指向dst的指针。其函数代码如下:

1     
2
3
4
5
6
7
8
char* strcpy(char* dst , char* src){     
if(dst==
NULL||src==
NULL)
return
NULL;
// --1
if(dst==src)
return dst;
//--2
char* address = dst;
//--3
while((*dst++ = *src++)!=
'\0')
//--4
return address;
//--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点可以用如下测试代码来说明:

1
int length =       
strlen(
strcpy(strA,strB));
//如果不支持链式表达式,这里会报错。

那么有时候也会问为什么不返回src的原始值,错误原因有以下三点:

  • 源字符串本来就已知,返回没有什么意义
  • 不能支持形如char * strA = strcpy(new char[10],strB) 这样的表达式
  • 为了保护源字符串,使用const限定了src所指的内容,把const char作为char的返回值,类型不符,编译器会报错。

strcpy和memcpy的不同点

这个也是常见的考点,主要分为以下三点不同:

  • 复制内容不同:strcpy只能复制字符串,而memcpy可以复制任何内容,例如字符数组,整型,结构体等
  • 复制的方法不同:strcpy不需要指定长度,它遇到字符串结束符’\0’才结束,所以容易溢出。memcpy则需要传入第三个参数来指定长度
  • 用途不同:通常在复制字符串的时候用strcpy,而需要复制其他数据类型的时候则一般用memcpy。

转载地址:http://fcrzn.baihongyu.com/

你可能感兴趣的文章
分布式和集群的区别
查看>>
本科毕设完整流程和注意事项
查看>>
不要想着憋大招,先完成每个小招
查看>>
ps中怎么把一张图片的一种颜色全部替换成另外一种颜色
查看>>
答辩PPT撰写和答辩注意事项
查看>>
第二次实习的实习总结
查看>>
PPT的一些常用操作
查看>>
Java面试题解析(基础篇,附答案)
查看>>
Spring 常用的三种注入方式
查看>>
MyBatis 一级缓存在分布式下的坑你知道吗?
查看>>
2020年2月JVM面试的30个知识点
查看>>
在家办公7天整理Spring Cloud知识点大全
查看>>
看看这些Java代码开发规范吧!你好,我好,大家好!
查看>>
2020年3月,47个经典Spring面试题详解(附带答案)
查看>>
python实现Mapreduce的wordcount
查看>>
Linux字符设备驱动编(步骤,框架(面向对象),分层)
查看>>
linux高级字符驱动之输入子系统
查看>>
代理与反射
查看>>
面向对象
查看>>
训练并导出tensorflow Lite模型中的一些问题 及解决办法
查看>>