C语言全部内存操作函数的实现详细讲解

 更新时间:2021年02月26日 11:10:02   作者:贫僧爱用飘柔  
这篇文章主要介绍了C语言全部内存操作函数的实现详细讲解,作者用图文代码实例讲解的很清晰,有感兴趣的同学可以研究下

memcpy内存拷贝函数

void* memcpy(void* destination, const void* source, size_t num);
  • memcpy函数从source的位置开始向后拷贝num个字节的数据到destination的内存位置
  • 这个函数在遇到\0的时候并不会停下来
  • 如果source和destination有任何的重叠,复制的结果都是未定义的

使用方法:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world!";
	int arr3[10] = { 0 };
	int arr4[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int i = 0;

	memcpy(arr1, arr2, 12);
	memcpy(arr3, arr4, 16);
	printf("%s\n", arr1);

	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr3[i]);
	}

	return 0;
}

输出结果:


image-20210224094511895

如果源头和目的地是同一块内存它进行拷贝的时候会出现覆盖的情况。

如:

#include <stdio.h>
#include <string.h>

int main()
{
 int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 int i = 0;
 
 memcpy(arr + 2, arr, 16);
 
 for (i = 0; i < 10; i++)
 {
 printf("%d ", arr[i]);
 }
 
 return 0;
}

image-20210224095909038

可以看到它并没有如我们预期的输出来输出结果,我们预期的结果应该是:1 2 1 2 3 4 7 8 9 10

可是memcpy拷贝的时候会覆盖,而C语言对memcpy的标准是只要能实现拷贝即可,不考虑同一块内存拷贝会覆盖的情况,这种情况是由另一个函数来处理。

当然有些编译器对memcpy函数的实现是有优化过的,目前我个人知道的编译器是VS它是对memcpy有优化的,如果拷贝的是同一块内存它不会覆盖,而是如预期的那样进行拷贝。

memcpy函数的实现

#include <assert.h>

void* my_memcpy(void* dest, const void* src, unsigned int count)
{
	assert(dest && src);//断言
	void* temp = dest;//temp保存dest的起始地址

	while (count--)
	{
		*(char*)dest = *(char*)src;//复制src的内容到dest
		++(char*)dest;//下一个字节的拷贝
		++(char*)src;
	}

	return temp;//返回dest起始地址
}

void* my_memcpy(void* dest, const void* src, unsigned int count);

参数一:void* dest

  • dest设置成空类型,因为空类型可以接收任何大小的数据,但是有一个缺陷它不能自增或者自减,也不能直接解引用因给它空类型是一个没有具体类型,它不知道它能访问多少个字节,所以使用空类型的时候我们需要强制类型转换。
  • dest是缓冲区

参数二:void* src

  • 它的类型和dest一样不过,它和参数一不同的是它被const保护起来了,因为它只是被复制也就是说我们只是访问它里面的内容并不需要修改它,所以我们就加一个const把它保护起来,防止我们不小心对它进行修改

参数三:unsigned int counst

  • counst是我们要修改多少字节的参数,修改是以字节为单位的,它的类型是unsigned int (无符号整整形)也就是说不能出现负数

返回类型:void*

  • 返回dest的首地址

assert(dest && src)这个是用来保证代码的健壮性,assert()函数是断言,如果传过来的是空指针,那么就是假因为NULL的值是0,只有两边都为真才不会有提示。

*(char*)dest = *(char*)src因为是void* 类型所以我们要强制转换才能解引用进行拷贝操作,而我们要操作的是一个字节所以转为字符型指针最合适。

++(char*)dest;和上面的同理,要强制类型转换才能进行++和–操作。


memmvoe函数

void* memmove(void* destination, const void* source, size_t num);
  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理

使用方法:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int i = 0;

	memmove(arr + 2, arr, 16);

	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

memmovememcpy的使用方法一样,没什么大区别。

memmove函数的实现

#include <assert.h>

void* my_memmove(void* dest, const void* src, unsigned int count)
{
	assert(dest && src);//断言
	void* temp = dest;

	if (dest < src)//小于src从前向后
	{
		while (count--)
		{
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++(char*)src;
		}
	}
	else//大于从后向前
	{
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
		}
	}

	return temp;
}

为了处理同块内存的拷贝,这里我们分为了两种方法。

  1. 从前向后拷贝
  2. 从后向后拷贝

memcpy用的是从前向后拷贝,所以会出现被覆盖的情况。

那么问题来了,我们什么情况才从前向后拷贝和从后向前拷贝呢?

image-20210224105035626

我们可以以src为分界线,如果dest小于src我们就从前向后,这样就避免了src的内容被覆盖之后被拷贝到dest里面去,如果dest大于src,我们就从后向前。

那有人问如果等于呢?等于的话你从前向后还是从后向前不都一样?

所以按照这个思路我们写成两个拷贝顺序,从后向前我们不用思考了,想在我们只需要思考从后向前拷贝。

	while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
		}

从后向前我们只需要先得到dest和src末尾的地址就能进行从后向前操作了,count + dest不就得到了末尾了吗?counst + dest得到末尾的\0的地址,但是我们不需要修改\0所以count + dest之前我们对count自减。

后面就不需要dest自减的操作了,因为count每次减一我们就得到前面一个的地址,当count减完了,我们也拷贝完了。

memcmp内存块比较函数

int memcmp(const void* ptr1, const void* ptr2, size_t num);
  • 比较从ptr1和ptr2指针开始的num个字节
  • 返回值,当ptr1大于ptr2就返回大于1的值,当ptr1小于ptr2就返回小于0的值,当等于的时候返回0

使用案列:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	char arr1[] = "abcz";
	char arr2[] = "abcd";

	if ( 0 < memcmp(arr1, arr2, 4))
	{
		printf("大于\n");
	}
	else if(0 > memcmp(arr1, arr2, 4))
	{
		printf("小于\n");
	}
	else
	{
		printf("等于\n");
	}

	return 0;
}

memcpy函数的实现

#include <assert.h>

int my_memcmp(void* dest, const void* src, unsigned int count)
{
	assert(dest && src);//断言

	if (!count)
	{
		return 0;
	}

	while (--count && *(char*)dest == *(char*)src)
	{
		++(char*)dest;
		++(char*)src;
	}

	return *(char*)dest - *(char*)src;
}
	if (!count)
	{
		return 0;
	}

如果count是0的话就直接返回0

while (count-- && *(char*)dest == *(char*)src)
	{
		++(char*)dest;
		++(char*)src;
	}

count个数比较完或者dest不等于src,我们就停止循环。

return *(char*)dest - *(char*)src;

直接返回dest - src,如果它们两相等一定返回0,dest小于src返回的是小于0的值,大于则返回大于0的值。

memset修改内存块

void *memset( void *dest, int c, size_t count )
  • dest是目的
  • 地第二个修改成什么?
  • 第三个修改内存的个数

memset是以1字节为单位来修改,第二个参数是要修改成什么字符,第三个参数是修改内存个数以1字节为单位

使用案列:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>

int main()
{
	char arr[10] = { 0 };
	int i = 0;

	memset(arr, '6', 10);

	for (i = 0; i < 10; i++)
	{
		printf("arr[%d]=%c\n", i, arr[i]);
	}

	return 0;
}

memset函数实现

#include <assert.h>

void* my_memset(void* dest, int a, unsigned int count)
{
	assert(dest);//断言
	void* temp = dest;//记录dest的首地址

	while (count--)
	{
		*(char*)dest = a;
		++(char*)dest;
	}

	return temp;//返回dest的首地址
}
while (count--)
{
	*(char*)dest = a;
	++(char*)dest;
}

把a的值给dest,来进行修改,每次修改一个字节就自增一修改下个字节。

到此这篇关于C语言全部内存操作函数的实现详细讲解的文章就介绍到这了,更多相关C语言内存操作函数的实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 12个关于C语言的有趣问答

    12个关于C语言的有趣问答

    这篇文章主要介绍了12个关于C语言的有趣问答,有助于读者加深对C语言程序设计的理解,需要的朋友可以参考下
    2014-07-07
  • C++模板之特化与偏特化详解

    C++模板之特化与偏特化详解

    这篇文章主要介绍了C++模板之特化与偏特化详解,本文讲解了什么是C++模板、模板特化、模板偏特化、特化与偏特化的调用顺序等内容,需要的朋友可以参考下
    2014-10-10
  • C++常见异常处理原理及代码示例解析

    C++常见异常处理原理及代码示例解析

    这篇文章主要介绍了C++常见异常处理原理及代码示例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • 详解C语言中的ttyname()函数和isatty()函数的用法

    详解C语言中的ttyname()函数和isatty()函数的用法

    这篇文章主要介绍了C语言中的ttyname()函数和isatty()函数的用法,是C语言入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • 深入了解C语言冒泡排序优解

    深入了解C语言冒泡排序优解

    这篇文章主要介绍了C语言冒泡排序法的实现(升序排序法),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • MFC之ComboBox控件用法实例教程

    MFC之ComboBox控件用法实例教程

    这篇文章主要介绍了MFC之ComboBox控件用法,包括了ComboBox控件常见的各类用法,非常具有实用价值,需要的朋友可以参考下
    2014-09-09
  • C语言中free函数的使用详解

    C语言中free函数的使用详解

    free函数是释放之前某一次malloc函数申请的空间,而且只是释放空间,并不改变指针的值。下面我们就来详细探讨下
    2017-05-05
  • C++的头文件和实现文件详解

    C++的头文件和实现文件详解

    这篇文章主要介绍了C++的头文件和实现文件详解的相关资料,需要的朋友可以参考下
    2015-01-01
  • 哈希表实验C语言版实现

    哈希表实验C语言版实现

    以下是对哈希表实验用C语言实现的代码进行了详细的分析介绍,需要的朋友可以参考下
    2013-07-07
  • C语言实现学生选修课程系统设计

    C语言实现学生选修课程系统设计

    这篇文章主要为大家详细介绍了C语言实现学生选修课程系统设计,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-02-02

最新评论