C/C++中mem和strcopy函数的区别和应用

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

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

//在C中
#include<string.h>
//在C++中
#include<cstring>
memcpy

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

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

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中存在重叠部分这种情况。

那么处理重叠部分的话,我们可以采用从后往前依次拷贝的方法,下面给出我修改过的函数代码:

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的值。其函数原型如下:

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而写下下面的代码:

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。

其实现代码如下:

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需要包含以下头文件:

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

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、这里有一个技巧,如果写成以下两种,面试的时候会大大扣分!
//第一种
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的不同点

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

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

时间: 2024-09-09 19:18:37

C/C++中mem和strcopy函数的区别和应用的相关文章

C/C++中的mem函数和strcopy函数的区别和应用_C 语言

strcpy和memcpy都是标准C库函数,它们有下面的特点. strcpy提供了字符串的复制.即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符. memcpy提供了一般内存的复制.即memcpy对于需要复制的内容没有限制,因此用途更广. mem系列函数是面试的时候常考的知识点,我们需要熟练掌握这三个函数的原理和代码实现,要能准确无误的写出代码. memcpy.memset和memset三个函数在使用过程中,均需包含以下头文件: //在C中 #include<

php中sprintf与printf函数用法区别

 这篇文章主要介绍了php中sprintf与printf函数用法区别解析,需要的朋友可以参考下 下面是一个示例:四舍五入保留小数点后两位    代码如下: <?php $num1 = 21; echo sprintf("%0.2f",$num1)."<br />"; //输出 21.00 $num2 = 16.3287; echo sprintf("%0.2f",$num2)."<br />";

JavaScript中各种编码解码函数的区别和注意事项_javascript技巧

大家在使用JS提交数据时,尤其是中文的时候,经常会需要将要提交的字符串进行URL编码.在JS中对字符串进行URL编码有好几种方 法,encodeURI,encodeURIComponent,还有escape.在我看到的很多代码中escape这个函数用的最多,不过这个函数却 是不推荐使用的.下面我们来分别看看这几个函数: encodeURI:对指定的字符串进行URL编码,不包括 : # / \ = & 这些URL中的关键字符. encodeURIComponent:对字符串中的字符进行编码,包括U

详解PHP中strlen和mb_strlen函数的区别_php实例

在PHP里有两个计算字符串个数的函数一个是 strlen,一个是mb_strlen;先来看看手册中的定义strlenstrlen - 获取字符串长度int strlen ( string $string )返回给定的字符串 string 的长度. mb_strlenint mb_strlen ( string $str [, string $encoding ] )返回给定的字符串 string 的长度.encoding参数为字符编码.如果省略,则使用内部字符编码. 这么看除了mb_strlen

PHP中strlen和mb_strlen函数的区别

在PHP中,strlen与mb_strlen是求字符串长度的函数,但是对于一些初学者来说,如果不看手册,也许不太清楚其中的区别- 先看例子: <?php   $str='中文a字1符';   echo strlen($str).'<br>';//14   echo mb_strlen($str,'utf8').'<br>';//6   echo mb_strlen($str,'gbk').'<br>';//8   echo mb_strlen($str,'gb23

php中explode与split函数的区别

首先来看下两个方法的定义: 函数原型:array split (string $pattern, string $string [, int $limit]) 函数原型:array explode ( string $separator, string $string [, int $limit]) 初看没有啥差别,貌似功能都一样.我就犯了这个错误. 请注意两个函数的第一个参数string $pattern和string separator,一个是$pattern说明是正则字符串,一个是$sep

Lua中使用.和:调用函数的区别_Lua

tb.print和tb:print的含义略微不同,:调用函数会多传递进去一个self进去,差不多相当于OOP里面的成员函数调用. 但是和OOP里面成员函数/非成员函数不同的是,lua里面的非成员函数.调用,他不是静态的. tb对象有一个print function成员,当tb是nil的时候,你不能调用print成员的....在OOP里面,没有这么一回事,非成员函数是全局的. 例子代码: 复制代码 代码如下: t = {x = 1}    t.foo = function(t)      prin

JS中confirm,alert,prompt函数使用区别分析_javascript技巧

window.alert:参数,只有一个,显示警告框的信息; 无返回值. <script> window.alert("确定.") </script> window.confirm : 参数就只有一个.显示提示框的信息. 按确定,返回true; 按取消返回false. <script> var bln = window.confirm("确定吗?"); alert(bln) </script> window.promp

iOS开发中常用的数学函数

iOS开发中常用的数学函数   /*---- 常用数学公式 ----*/ //指数运算 3^2 3^3 NSLog(@"结果 %.f", pow(3,2)); //result 9 NSLog(@"结果 %.f", pow(3,3)); //result 27 //开平方运算 NSLog(@"结果 %.f", sqrt(16)); //result 4 NSLog(@"结果 %.f", sqrt(81)); //result