Introduction of memory functions such as memcpy and memmove in C language

Introduction of memory functions such as memcpy and memmove in C language

Previously, we introduced some string operation functions Introduction of character operation function , strings in c language are treated as arrays, but those functions can only operate on strings. Are there any functions that can operate on other types of arrays?

Today we will introduce some such functions

perror

Let's introduce perror here

Function prototype of perror:

The previous blog talked about the strerror function. The error code errno is passed into strerror as a parameter, and the function will return the address of the error information, which can be printed out

perrorr does not need to actively transmit errno when using. You can write the content you want to add in parentheses. When printing error messages, the added content will be added. Unlike strerror, strerror converts error codes into error messages without printing.

Example:

strerror:

perror:

memcpy

Introduction to memcpy function

Function prototype:

void * memcpy ( void * destination, const void * source, size_t num ); 

The function returns the address of the target space

strcpy mentioned in the previous blog is only applicable to the copy of strings, not all types of elements, while memcpy is applicable to the copy of all types of elements, and the number of elements (bytes) can be specified

Similar to strncpy

The function memcpy copies num bytes of data back to the memory location of destination from the location of source.

This function will not stop when it encounters' \ 0 '.

If there is any overlap between source and destination, the copied result is undefined.

In other words, if the two spaces overlap, the copy will fail

Let's look at an example:

#include <stdio.h> 

#include <string.h> 

struct { 

 char name[40]; 

 int age; 

} person, person_copy; 

int main () 

{ 

 char myname[] = "Pierre de Fermat"; 

 /* using memcpy to copy string: */ 

 memcpy ( person.name, myname, strlen(myname)+1 ); 
    //Copy the contents of myname to the name of person in the structure
    //strlen(myname)+1 means that \ 0 is also copied in

 person.age = 46; 

 /* using memcpy to copy structure: */ 

 memcpy ( &person_copy, &person, sizeof(person) ); 
    //Copy the contents of the structure person to person_copy

 printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age ); 

 return 0; 

} 

So the print result should be: person_copy: Pierre de Fermat, 46

Similarly, we simulate memcpy:

Since memcpy can receive any type of data, the parameters of the function should be set to null type. The detailed description is mentioned in another article of the author about the implementation of * * * callback function * * * simulating qsort c pointer summary

void * can receive any type of address, but can't perform operations such as dereference. How to operate the data? The size of each type is different. Similarly, this implementation method is the same as that of qosrt. It only needs to forcibly convert the two formal parameters to char * type, then dereference and exchange each byte, so that any type of data can be exchanged, and the third parameter is the number of bytes to be exchanged, Therefore, there is a disadvantage here, that is, users need to accurately write the number of bytes of the content to be copied, otherwise they are prone to errors. Here's the code:

void* my_memcpy(void* dst, const void* src, size_t num)//Use void * type pointers to receive various types of pointers
{
	assert(dst && src);
	void* start = dst;
	while (num--)//Cycle num times and exchange num bytes
	{
		*(char*)dst = *(char*)src;//Because the element type to be exchanged is uncertain, it is forced with the char * pointer, and then exchanged byte by byte
		dst = (char*)dst + 1;//After the exchange, add 1 to the address
		src = (char*)src + 1;
	}
	return start;
}

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = {0};
	my_memcpy(arr2, arr1, 20);
	return 0;
}

One thing to note:

*(char*)dst = *(char*)src;//Because the element type to be exchanged is uncertain, it is forced with the char * pointer, and then exchanged byte by byte
		dst = (char*)dst + 1;//After the exchange, add 1 to the address
		src = (char*)src + 1;

This paragraph cannot be written as * (char*)dst++ = (char)src + +;

Forced conversion is only a temporary state, and the effect may be gone by the time of + +, which depends on how the compiler handles it

However, this implementation method cannot complete the copy when the target space overlaps with the source space

void* my_memcpy(void* dst, const void* src, size_t num)//Use void * type pointers to receive various types of pointers
{
	assert(dst && src);
	void* start = dst;
	while (num--)//Cycle num times and exchange num bytes
	{
		*(char*)dst = *(char*)src;//Because the element type to be exchanged is uncertain, it is forced with the char * pointer, and then exchanged byte by byte
		dst = (char*)dst + 1;//After the exchange, add 1 to the address
		src = (char*)src + 1;
	}
	return start;
}

//

int main()
{
	int arr1[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	my_memcpy(arr1 + 2, arr1, 20);
	return 0;
}

The original intention of this code is to copy 12345 to 34567. The immediate results are: 1, 2, 1, 2, 3, 4, 5, 8, 9, 10

But the actual situation:

The results were not as good as expected

Let's use the library function to realize the following:

Find that the result is what we want. Why?

Let's go on and solve these problems when explaining memmove

memmove

Introduction to memmove function

Function prototype:

void * memmove ( void * destination, const void * source, size_t num ); 

The parameter type and return type are the same as those of memcpy function

The difference from memcpy is that the source memory block and target memory block processed by memmove function can overlap.

If the source space and target space overlap, you have to use the memmove function.

#include <stdio.h> 

#include <string.h> 

int main () 

{ 

 char str[] = "memmove can be very useful......"; 

 memmove (str+20,str+15,11); //Copy 11 characters after v from u

 puts (str); 

 return 0; 

} 

The result should be: memmove can be very useful

Of course, this is also the result of using the library function memcpy

Let's first simulate the implementation of memmove

There are two situations for this copy:

One is that the source address is greater than the target address, and the other is that the source address is less than the target address

The copy methods adopted in different situations are also different

But the premise is that the target space coincides with the source space

When we implement memcpy, we copy the data from the source space to the target space from front to back

Let's analyze the different copy methods in different situations:

Source address is greater than destination address:

The copy from back to front and copy from front to back mentioned above refer to the source space. When actually copying, the bytes behind the source space are still assigned to the bytes behind the corresponding target space, and the bytes in front of the source space are still assigned to the bytes in front of the corresponding target space

In fact, there is another situation:

This situation will be understood after we implement the code

void* my_memmove(void* dst, void* src, size_t num)
{
	assert(dst && src);
	void* start = dst;
	if (src > dst)  //The source address is greater than the destination address
	{
		while (num--)//Same as implementing memcpy
		{
			*(char*)dst = *(char*)src;
			dst = (char*)dst + 1;
			src = (char*)src + 1;
		}
	}
	if (dst > src) //The destination address is greater than the source address
	{
		while (num--)//Copy from the last byte of the last element to the first byte of the first element
		{
			*((char*)dst + num) = *((char*)src + num);
		}
	}
	return start;
}

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

Therefore, in the third case, * ((char *) DST + Num) = * ((char *) SRC + Num) will cause the array to cross the boundary. If the memory behind the array is not large enough, it will cause some trouble.

Note: the memcpy function provided by some compilers has the same function as memmove, that is, it can copy the space when the source memory and target memory overlap, such as vs2019

So we know why we can use the library function memcpy to realize the function of memmove

The methods of copying are different in different cases, so the memcpy we simulated can not copy the data when the space overlaps

memcmp

Introduction to memcmp

Function prototype:

int memcmp ( const void * ptr1, 

 const void * ptr2, 

 size_t num ); 

The function parameters and return types are the same as those of the above two functions

memcmp can compare num bytes starting from ptr1 and ptr2 pointers

The return values are as follows:

use:

#include <stdio.h> 

#include <string.h> 

int main () 

{ 

 char buffer1[] = "DWgaOtP12df0"; 

 char buffer2[] = "DWGAOTP12DF0"; 

 int n; 

 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; 

} 

Observe the two arrays and find that G and G are different. The ASCII code value of G is greater than that of G, so the result should be '% s' is greater than'% s'

Similarly, the simulation implementation of memcmp is very similar to the implementation of compare function in qsort function:

int     my_memcmp(void* buf1, void* buf2, int count)
   {
	if (!count)//When count equals 0, 0 is returned directly
		return(0);
	while (--count && *(char*)buf1 == *(char*)buf2)//One byte is equal and count= When 0, add 1 byte to the address
	{
		buf1 = (char*)buf1 + 1;
		buf2 = (char*)buf2 + 1;
	}
	return(*(char*)buf1 - *(char*)buf2);//When it reaches the last byte, it directly returns the difference between the memory values of the two bytes
}

int main()
{
	float arr1[20] = { 1.0,2.0,3.0,5.0,7.0 };
	float arr2[20] = { 1.0,2.0,3.0,4.0,5.0 };
	int ret = my_memcmp(arr1, arr2, 12);
	printf("%d\n", ret);
	return 0;
}

memset

Introduction to memset function

Function prototype:

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

The function parameter of memset is a null pointer receiving array. Value refers to the value to be modified. num is also the number of bytes

memset sets the memory in bytes

Therefore, if the memory of int is set, the result cannot reach all 1

Example:

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

Because 1 is 01 in hexadecimal in memory, setting memory for int type means that each memory is set to 01

Analog implementation of memset:

void* mymemset(void* ptr, int value, size_t num)
{
	assert(ptr);
	void* start = ptr;
	if (!num)
		return 0;
	while (num--)
	{
		*(char*)ptr = (char)value;//Set the size of each byte
        //Strong conversion of value, otherwise data will be lost
		ptr = (char*)ptr + 1;
	}
	return start;
}

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

The above is a brief introduction to some memory functions. I hope it will be helpful to you.

Keywords: C

Added by bfuzze_98 on Thu, 03 Feb 2022 00:46:44 +0200