返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言内存操作函数使用示例梳理讲解
  • 739
分享到

C语言内存操作函数使用示例梳理讲解

2024-04-02 19:04:59 739人浏览 泡泡鱼
摘要

目录一、memcpy()函数原型参数说明模拟算法使用示例二、memmove()函数原型参数说明模拟算法使用示例三、memset()函数原型参数说明使用说明使用示例四、memcmp()

一、memcpy()

函数原型

void * memcpy ( void * dest, const void * src, size_t num );

参数说明

  • 函数 memcpy 从 src 位置开始向后复制 num 个字节的数据到 dest 的内存位置。
  • void * dest 代表目标的内存地址,const void * src 代表源内存地址。其中二者的数据类型均为 void * 。void * 可以存储任何类型地址的值。因此,该函数拷贝的内存数据可以是任意类型(如果int、float、double等均可)。
  • size_t 即unsigned int类型。注意:num代表的是要拷贝的字节数,而非元素个数!如要从src拷贝一个int类型的数据到dest,则num应传入4,而非1.
  • 该函数的返回值类型也为 void * ,也即只返回目标地址的数值。后续如何按照基类型取出数据、使用数据,可以由调用方在调用函数后实现。

模拟算法

void* my_memcpy(void* dest, const void* src, size_t num) {
    void *ret = dest;    //保存dest,用于最终输出
    assert(dest);
    assert(src);
	while (num--) {
		*(char*)dest = *(char*)src1;    //(char*): 取出每一个字节(8 bit)的值,以单个字节为单位进行赋值
		dest = (char*)dest + 1;    
		src = (char*)src + 1;
	}    //void * 类型是不能直接运算的,因为没有步长。(char*)将dest与src转换为以char为步长,再向后移动
	return ret;
}
  • 该函数用于实现没有重叠部分内存的内存数据拷贝。如果source和destination有任何的重叠,复制的结果都是未定义的。
  • 拷贝内存值按从低地址到高地址的顺序进行,多用于数组

根据memcpy()的模拟算法,如果src与dest的内存空间有重叠部分,则可能导致src中的内容被覆盖,无法输出正确的值。

src中元素3的位置恰好也是dest中首元素的位置。dest的首元素被更改的同时,src中的元素也被更改。因此,memcpy()是不可用于“自己拷贝到自己后面”这样的操作的。这一问题留给了memmove()来解决。

使用示例

1.简单

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, 20);    //拷贝20个字节,即5个int元素
	float arr3[] = { 1.0f,2.0f,3.0f,4.0f };
	float arr4[5] = { 0.0 };
	memcpy(arr3, arr4, 8);    //拷贝8个字节,即2个float元素
	return 0;
}

2.进阶

//示例来自cplusplus官网

#include <stdio.h>
#include <string.h>
struct {
  char name[40];
  int age;
} person, person_copy;
int main ()
{
  char myname[] = "Pierre de Fermat";    //定义一个字符串
  
  //每个char类型占一个字节,因此要拷贝的字节数即strlen()+1,加一是因为要把'\0'也拷贝过去。
  memcpy ( person.name, myname, strlen(myname)+1 );
  person.age = 46;
  
  //sizeof操作符,可以直接得到结构体变量在内存中所占的字节数。
  memcpy ( &person_copy, &person, sizeof(person) );
  //直接完成了结构体之间的数据拷贝:从person拷贝到person_cpy,不用手动转义,非常方便
  printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
  return 0;
}

二、memmove()

函数原型

void * memmove ( void * dest, const void * src, size_t num );

参数说明

  • 该函数的参数与返回值类型与memcpy()函数相同。该函数同样用作 从 src 位置开始向后复制 num 个字节数据到 dest 的内存位置。
  • memmove()函数与memcpy()函数主要的区别在于memmove()可以进行有内存重叠的数据拷贝,而memcpy()绝对不能。memmove()的功能比memcpy()更加完善。
  • 可以理解为:如果memcpy()函数够到了60分,那么memmove()函数却能到达90分。

模拟算法

#include<stdio.h>
#include<string.h>
//模拟实现memmove()
void* my_memmove(void* dest, const void* src, size_t num) 
{
	void* ret = dest;	//保存结果用于输出
	//从前向后拷贝,也可以写成if(dest <= src || (char*)dest >= (char*)src + num)
	if (dest <= src)
	{
		while (num--) {
			*(char*)dest = *(char*)src;    //拷贝
			dest = (char*)dest + 1;   
			src = (char*)src + 1;     //指针从前向后移动(从低地址向高地址移动)
		}
	}
	else    //从前向后拷贝
	{
		dest = (char*)dest + num - 1;    
		src = (char*)src + num - 1;    //初始化两指针至各自范围的最后
		while (num--) {
			*(char*)dest = *(char*)src;    //拷贝    
			dest = (char*)dest - 1;    
			src = (char*)src - 1;    //指针从后向前移动
		}
	}
	return ret;    //返回值为dest
}
//测试代码///
int main() {
	int arr1[] = { 2,3,4,5,6 };
	my_memmove(arr1+2, arr1, 8);
	//预计 2 3 2 3 6
	for (int i = 0; i < 5; i++) {
		printf("%d ", arr1[i]);
	}
	printf("\n-------------------\n");
	int arr2[] = { 2,3,4,5,6 };
	memmove(arr2 + 2, arr2, 8);
	for (int i = 0; i < 5; i++) {
		printf("%d ", arr2[i]);
	}
	return 0;
}

拷贝顺序的结论如图所示。上面提到,当有内存重叠时,拷贝的顺序是有讲究的。若不遵守下图的结论, 仍将导致src中原来的值被覆盖,无法输出正确的结果。

***错误的模拟算法

如下代码是错误的:

//错误的代码
void* my_memmove(void* dest, const void* src, size_t num)
{
    void * ret = dest;
    while (num--) {
	//从前向后拷贝
	    if (dest <= src)
	    {
		    *(char*)dest = *(char*)src;
		    dest = (char*)dest + 1;
	    	src = (char*)src + 1;
	    }
	    else
	    {
		    dest = (char*)dest + num - 1;    //错误
		    src = (char*)src + num - 1;    //错误
		    *(char*)dest = *(char*)src;
		    dest = (char*)dest - 1;
		    src = (char*)src - 1;
	    }
    return ret;
}

有一些同学可能认为,while(num--)语句与if语句的顺序可以调换。正确的代码是先进行情况判断,再进入while(num--)进行赋值与移动。将二者顺序调换,乍一看没有什么问题,是先设定一共要移动num次,再进入判断进行具体的操作。但是这样书写是有问题的,因为在dest > src && dest < src+num时,需要从后向前拷贝,这意味着dest和src的起始位置要发生变化。

上述代码中标记“错误”的语句便是dest和src初始化的语句。如果直接将while和if的位置调换,则每一次进入循环,都要初始化一遍dest和src。如此,dest与src的功能就被打乱了。

使用示例

//示例来自cplusplus官网

#include <stdio.h>
#include <string.h>
int main ()
{
  char str[] = "memmove can be very useful......";
  memmove (str+20,str+15,11);    //表示将包括str+15向后11个字节的内容移动到str+20位置
  puts (str);
  return 0; 
}

输出:

如下图,将 src = str+11 位置开始,包括该位置共向后拷贝11字节。每个char占一个字节,因此拷贝了"very useful"这7个char字母至dest = str+20的位置。

三、memset()

函数原型

void * memset ( void * ptr, int value, size_t num );

参数说明

  • 第一个参数 ptr 为指针类型,表示要进行操作的内存的地址。如要对数组arr进行内存内容设置,则该参数的值为arr。
  • 第二个参数 value 为要设定的内存的值。该值的数据类型是int型,但char值也是可以的。
  • 第三个参数 num 为要设置值的内存的字节数。注意:是字节数,而不是元素的个数。如要改变两个int类型的值,num应为 8 ,而不是2.

使用说明

例如,有数组arr:[ 1 2 3 4 5 ]

要将其前两个元素值设定为0,则可使用memset函数:memset(arr, 0 , 8)

输出:[ 0 0 3 4 5 ].原理如下(以小端存储的形式展现):

arr数组为int类型,一个int为4字节、32bit,内存中的存储如下(二进制):

01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

memset(arr, 0, 8),将前8个字节(2个int)置为0(其实每个比特位都被置为0了,但由于其它的比特位已经为0,故没有标出来)

但若使用 memset(arr, 1, 8),并不是把数组前两个元素置为 1 .因为memset()函数是针对内存中每个字节的,memset(arr, 1, 8)的实际作用是将前8个字节中的内容全部置为1.

00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01

00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01

03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

此时,前4字节按照int类型解析出来,结果为1000000010000000100000001,即16843009。

总结

  • int类型数组除了置0外,用memset置换成任何数都是错的。
  • memset只适用于每个元素只占1个字节的数组,比如char型数组。因为memset的操作单位就是每个字节。只有char类型的数组不会出现错误。

使用示例

//示例来自cplusplus官网

#include <stdio.h>
#include <string.h>
int main ()
{
  char str[] = "almost every programmer should know memset!";
  memset (str,'-',6);    //表示将从str开始,包括str向后6个字节的内存内容置为'-'
  puts (str);
  return 0;
}

//输出:------ every programmer should know memset!

四、memcmp()

函数原型

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

参数说明

  • 比较ptr1和ptr2指针开始的num个字节。
  • ptr1和ptr2分别是两个代表要比较的内存空间(一般是数组)的指针。
  • num是要比较的字节数。(注意:不是元素个数)。

使用说明

返回值为整型,若返回值>0,则ptr1的内存长度大于ptr2;若返回值==0,则二者相等;若返回值<0,则ptr1的内存长度小于ptr2

使用示例

//示例来自cplusplus官网

#include <stdio.h>
#include <string.h>
int main ()
{
  //创建两个要用作比较的数组
  char buffer1[] = "DWgaOtP12df0";    
  char buffer2[] = "DWGAOTP12DF0";
  //接受比较的结果
  int n;
  //要比较的字节数为buffer1的长度
  //两字符串的比较可以用strcmp(buffer1,buffer2)函数实现,原理大致相同。
  n = memcmp ( buffer1, buffer2, sizeof(buffer1) );
  if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
  else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
  else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
  return 0;
}

到此这篇关于C语言内存操作函数使用示例梳理讲解的文章就介绍到这了,更多相关C语言内存操作函数内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言内存操作函数使用示例梳理讲解

本文链接: https://lsjlt.com/news/168130.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C语言内存操作函数使用示例梳理讲解
    目录一、memcpy()函数原型参数说明模拟算法使用示例二、memmove()函数原型参数说明模拟算法使用示例三、memset()函数原型参数说明使用说明使用示例四、memcmp()...
    99+
    2024-04-02
  • C语言内存操作函数详解
    目录头文件:#include<memory.h>1.memcpy2.memmove3.memcmp4.memset总结头文件:#include<memory.h&g...
    99+
    2024-04-02
  • C语言深入讲解内存操作问题
    目录一、野指针二、野指针的由来三、基本原则四、小结-上 五、常见的内存错误六、内存操作的规则七、小结-下 一、野指针 指针变量中的值是非法的内存地址,进而形成野指...
    99+
    2024-04-02
  • C语言详细分析讲解内存管理mallocreallocfreecalloc函数的使用
    目录C语言内存管理一、动态空间申请二、动态空间的扩容三、释放内存C语言内存管理 malloc && realloc && free &&...
    99+
    2024-04-02
  • C语言数组的各种操作梳理
    目录一、一维数组1.创建2.初始化3.使用4.数组在内存中的存储5.数组大小的计算二、二维数组1.创建2.初始化3.使用4.二维数组在内存中的存储三、数组作为函数参数1.关于数组名是...
    99+
    2024-04-02
  • C语言编程C++动态内存分配示例讲解
    目录动态内存管理为什么存在动态内存分配动态内存函数的介绍malloc申请空间和free释放空间有借有还 free释放内存calloc申请内存realloc调整动态内存的大小reall...
    99+
    2024-04-02
  • C语言深入讲解动态内存分配函数的使用
    目录一、malloc二、free(用于释放动态开辟的空间)三、calloc四、realloc五、常见的动态内存分配错误六、柔性数组局部变量和函数的形参向栈区申请空间 全局变量和sta...
    99+
    2024-04-02
  • C语言实例梳理讲解常用关键字的用法
    目录一、C语言关键字详解1. sizeof2. const3. static4. extern5. volatile6. typedef7. enum8. continue9. br...
    99+
    2024-04-02
  • C语言字符串函数与内存函数精讲
    目录strlenstrcpystrcatstrcmpstrncpystrncatstrncmpstrstrstrtokstrerrortolower\touppermemcpymem...
    99+
    2024-04-02
  • C语言超详细讲解字符串函数和内存函数
    目录字符串函数长度不受限制的字符串函数strlenstrcpystrcatstrcmp长度受限制的字符串函数介绍strncpystrncatstrncmp字符串查找以及错误报告str...
    99+
    2024-04-02
  • C语言深入讲解函数的使用
    目录关于函数1. 函数的定义形式2. 函数的声明3. 返回语句4. 函数参数4.1 形式参数(传值调用)4.2 实际参数(传址调用)4.3 无参数5. 函数的调用5.1 嵌套调用5....
    99+
    2024-04-02
  • C语言全面讲解顺序表使用操作
    目录一、顺序表的结构定义二、顺序表的结构操作1.初始化2.插入操作3.删除操作4.扩容操作5.释放操作6.输出三、示例编程环境为 ubuntu 18.04。 顺序表需要连续一片存储空...
    99+
    2024-04-02
  • GO语言字符串处理Strings包的函数使用示例讲解
    目录常用的字符串处理函数(1) Contains(2) Join(3) Index(4) Repeat(5) Replace(6) Split(7) Trim(8) Fields字符...
    99+
    2024-04-02
  • MySQL数据管理操作示例讲解
    目录外键DML语言添加 insert修改 update删除 delete外键 方式一:在创建表的时候,增加约束 删除有外键的表的时候,要先删除引用外键的表 物理外键:不建议使用,数据库级别的外键,不建议使用!(...
    99+
    2022-12-22
    MySQL数据管理 SQL数据管理
  • C语言中单目操作符++、–的实例讲解
    目录前言++单目操作符- -单目操作符附1:代码演示:演示代码提取:总结前言 大家先判断下下面代码的运行结果 答案: 如果你全对了,那么恭喜大佬,这篇博客可能对你收益不大,全...
    99+
    2024-04-02
  • C语言动态内存函数详解
    目录动态开辟空间的原因 1、malloc函数2、free函数3、calloc函数4、realloc函数总结动态开辟空间的原因 静态开辟空间是固定的,数组在申明的时候必须指定数组的长...
    99+
    2024-04-02
  • C语言详细讲解qsort函数的使用
    目录qsort1.int型2.float型3.struct型qsort 功能:Performs a quick sort.(快速排序)参数:void qsort( void *bas...
    99+
    2024-04-02
  • C语言深入讲解函数参数的使用
    目录一、函数参数二、程序的顺序点三、小结-上四、调用约定五、可变参数六、可变参数的限制七、小结-下一、函数参数 函数参数在本质上与局部变量相同在栈上分配空间函数参数的初始值是函数调用...
    99+
    2024-04-02
  • C语言动态内存管理malloc函数怎么使用
    这篇文章主要讲解了“C语言动态内存管理malloc函数怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言动态内存管理malloc函数怎么使用”吧!1.C语言动态内存管理库函数介绍1...
    99+
    2023-07-04
  • C语言示例讲解ifelse语句的用法
    目录1、前言2、if语句的语法结构3、关于if else语句的示例4、if else 书写形式的对比5、例子1、前言 (1)C语言是结构化的程序设计语言。C语言的三种基本程序结构分别...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作