首頁 > 軟體

C語言全部記憶體操作函數的實現詳細講解

2021-02-26 13:01:21

memcpy記憶體拷貝函數

void* memcpy(void* destination, const void* source, size_t num);
  • memcpy函數從source的位置開始向後拷貝num個位元組的資料到destination的記憶體位置
  • 這個函數在遇到的時候並不會停下來
  • 如果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("%sn", 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得到末尾的的地址,但是我們不需要修改所以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]=%cn", 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語言記憶體操作函數的實現內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com