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;
}

输出结果:

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

如:

#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;
}

可以看到它并没有如我们预期的输出来输出结果,我们预期的结果应该是: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用的是从前向后拷贝,所以会出现被覆盖的情况。

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

我们可以以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语言内存操作函数的实现内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言多线程服务器的实现实例

    本文基于 C 标准库提供的网络通信 API,使用 TCP ,实现一个简单的多线程服务器 Demo . 首先要看 API API 字节序转换 函数原型: #include <arpa/inet.h> uint64_t htonll(uint64_t hostlonglong); uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint64_t ntohll(uint64_t netlonglong);

  • C语言 数据存储方式知识点详解

    C语言 数据存储方式 一.源码 一个数的原码(原始的二进制码)有如下特点: 最高位做为符号位,0表示正,为1表示负 其它数值部分就是数值本身绝对值的二进制数 负数的原码是在其绝对值的基础上,最高位变为1 下面数值以1字节的大小描述: 十进制数 原码 +15 0000 1111 -15 1000 1111 +0 0000 0000 -0 1000 0000 注:原码表示法简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减

  • C语言数据的存储和取出详细讲解

    数据的存储和取出 整形的储存 我们知道一个整形的存储是以补码的形式储存取出是原码的形式. 比如:int a = 5;的二进制是101 那它的原码应该是:00000000 00000000 00000000 00000101 正数的原反补相同那它存进去和取出来都是:00000000 00000000 00000000 00000101 那float a = 5.5;也是四个字节它和整形存储的方式一样吗? 浮点型的储存方式 例子: #define _CRT_SECURE_NO_WARNINGS 1

  • C语言指针基础知识实例讲解

    对程序进行编译的时候,系统会把变量分配在内存单位中,根据不同的变量类型,分配不同的字节大小.比如int整型变量分配4个字节,char字符型变量分配1个字节等等.被分配在内存的变量,可以通过地址去找到,内存区每一个字节都有一个编号,地址也可以形象的理解成我们生活中的住址,通过住址找到每一个人所在的地方.指针作为一个变量用来存放地址,可以通过指针来改动变量. 上图就是一个简单的定义一个一级指针变量和利用指针改变变量数值的过程.int*表示整型指针,*p表示解引用操作,就是利用指针找到a的地址然后再改

  • 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> #in

  • C语言动态内存分配函数的实现

    在C中我们开辟内存空间有两种方式 : 1.静态开辟内存 :例如: int a;int b[10]; 这种开辟内存空间的特点是 所开辟的内存是在栈中开辟的固定大小的 ,如a是4字节 ,数组b是40字节 ,并且数组在申明时必须指定其长度 , 如果是全局数组的话,内存是在编译时分配好的,如果是局部变量数组的话,运行时在栈上静态分配内存.不管是全局数组还是局部数组,它们都有一个特点,那就是数组大小是确定的,是代码中写死的.那如果我们想在程序运行时才确定一个数组的大小 , 前两种在栈上分配内存的方法显然是

  • C语言实现字符串操作函数的实例

    C语言实现字符串操作函数的实例 在编写程序的过程中,我们经常使用到一些字符串函数,例如求字符串长度,拷贝字符串--,这些函数都在C标准库中存在,我们可以直接使用.但我们还需要掌握这些函数的实现方法,今天来看看一些常用的字符串操作函数的实现方法. 1.strlen strlen是用来求字符串长度的函数,字符串长度就是它所包含的字符个数. 今天给大家介绍三种实现strlen函数的方法 (1)定义一个计数器count //方式一:定义一个计数器 size_t my_strlen(const char

  • C语言函数声明以及函数原型超详细讲解示例

    C语言代码由上到下依次执行,原则上函数定义要出现在函数调用之前,否则就会报错.但在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明. 所谓声明(Declaration),就是告诉编译器我要使用这个函数,你现在没有找到它的定义不要紧,请不要报错,稍后我会把定义补上. 函数声明的格式非常简单,相当于去掉函数定义中的函数体,并在最后加上分号;,如下所示: dataType functionName( dataType1 param1, dataType2 param2 ... ); 也

  • C++类模板与函数模板基础详细讲解

    目录 函数模板 类模板 总结 函数模板 当我们想要定义一个可以支持泛型的函数时,就要采用函数模板的方式了.所谓泛型就是可以支持多种类型的操作,比如我们定义一个compare操作,他可以根据传递给他的参数类型动态调用对应的函数版本,实现多种类型的比较. template <typename T> int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1;

  • C语言内存操作函数详解

    目录 头文件:#include<memory.h> 2.memmove 3.memcmp 4.memset 总结 头文件:#include<memory.h> 1.memcpy 作用:内存拷贝 函数原型: void *memcpy( void *dest, const void *src, size_t count ); 使用: 使用格式:memcpy(目的地,原,想操作内存大小(单位字节)) 把 "参数2" 起始的 "参数3" 个字节 内容

  • C语言文件操作函数大全(超详细)

    fopen(打开文件)相关函数 open,fclose表头文件 #include<stdio.h>定义函数 FILE * fopen(const char * path,const char * mode);函数说明 参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态.mode有下列几种形态字符串:r 打开只读文件,该文件必须存在.r+ 打开可读写的文件,该文件必须存在.w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失.若文件不存在则建立该文件.w

  • C字符串操作函数的实现详细解析

    1. strlen(),计算字符串长度   int strlen(const char string) { int i=0; while(string[i]) i++; return i; } 2. strcpy(), 字符串拷贝. char *strcpy(char *destination, const char *source) { while(*destinaton++=*source++); return (destination-1); } 3. strcat(), 字符串的连接.

  • C语言中的字符(char)详细讲解

    1.字符型(char)简介 字符型(char)用于储存字符(character),如英文字母或标点. 严格来说,char 其实也是整数类型(integer type),因为 char 类型储存的实际上是整数,而不是字符. 计算机使用特定的整数编码来表示特定的字符. 2. 声明字符型变量 3. 字符常量与初始化 实例: 用 char 类型来专门表示一个字符,例如: char a='1'; char b='$'; char c='X'; char d=' '; // 空格也是一个字符 char e=

  • C语言超详细讲解字符串函数和内存函数

    目录 字符串函数 长度不受限制的字符串函数 strlen strcpy strcat strcmp 长度受限制的字符串函数介绍 strncpy strncat strncmp 字符串查找以及错误报告 strstr strtok strerror 内存操作函数 memcpy memmove memcmp 字符串函数 长度不受限制的字符串函数 strlen size_t strlen ( const char * str ) 求字符串长度: 字符串以'\0' 作为结束标志,strlen函数返回的是在

随机推荐