【C语言】内存函数的概念,使用及模拟实现

05-13 1411阅读

Tiny Spark get dazzling some day.

目录

  • 1. memcpy
    • -- 函数原型
    • -- 函数使用
    • -- 函数的模拟实现
    • 2.memmove
      • -- 函数原型
      • -- 函数使用
      • -- 函数的模拟实现
      • 3. memset
        • -- 函数原型
        • -- 函数使用
        • -- 函数的模拟实现
        • 4. memcmp
          • -- 函数原型
          • -- 函数使用
          • -- 函数的模拟实现

            1. memcpy

            • 使用需包含头文件:

              – 函数原型

              #include  // 头文件
              void* memcpy ( void* destination, const void* source, size_t num ); 
              			       目标空间               源内容         拷贝数目
              

              memcpy 函数的作用,是将一块空间(源)的前 num 个字节 的内容(注意!是字节不是字符个数),赋值粘贴到另一个空间(目标)。

              规则:

              1. 源指针 和 目标指针 指向的对象的 基础类型 与此函数无关,即: memcpy函数对任意类型的数据进行操作,例如字符串,数组和结构体等。
              2. memcpy 函数 不会检测 源内容中是否存在结束标志 ’ \0 ',要求拷贝多少字节的内容就精确地拷贝多少字节的内容。
              3. 为避免溢出,两个参数 所指向的空间最好不是同一个(否则可能会造成重叠问题,这是更推荐使用的是 memmove 函数)。
              4. 确保 目标空间 有足够位置存放要复制的内容。

              这哥们有点和 字符串函数的 strcpy 函数类似,都是把一段内容复制粘贴到另一个空间。不同的是 strcpy 只能复制 字符串内容, 而 memcpy 能复制任意类型的内容。


              – 函数使用

              int dest_arr[10]; 
              int src_arr[] = { 1, 2, 3, 4, '\0', 5, 6, 7, 8, 9 };
              int sz = sizeof(src_arr) / sizeof(src_arr[0]);
              memcpy(dest_arr, src_arr[], sz * sizeof(int));// 把整个源数组的内容都复制过去
              此时 dest_arr[10] 里面的内容就是 1, 2, 3, 4, 0, 5, 6, 7, 8, 9
              

              memcpy 并没有到 ’ \0 '停止了而是继续复制,我理解为函数并没有去检测 ’ \0 ’ 的含义,而是仅仅将其当作数组元素一个数据而已。

              我们再来看看第二种—复制字符串:

              char dset_str[20];
              char src_str[] = "Jackie\0Chan";
              int sz = sizeof(src_str) / sizeof(src_str[0]);
              memcpy(dest_str, src_str, sz * sizeof(char);// 把整个源数组的内容都复制过去
              //此时 dest_arr[10] 里面的内容就是 Jackie\0Chan
              printf("%s", dest_str);
              // 但是,在 printf 打印的时候不要误跟着以为 也会不检测'\0'而打印 "Jackie\0Chan"
              // 从而可能一脸懵逼 (me),或者 "这不常识?~不屑" (大佬you)
              

              第三种–如果 源空间 和 目标空间 重叠时使用情况:

              int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10 };
              memcpy(arr + 2, arr, 8 * sizeof(int));// 源空间开头为 第三个元素的位置 
              for(int i= 0; i  
              

              在VS2022上可能直接用可能会正常打印 1 2 1 2 3 4 5 6 7 8

              但如果用 模拟函数实现的方法 执行程序:

              【C语言】内存函数的概念,使用及模拟实现

              这种是错误的

              下面来解释下:

              【C语言】内存函数的概念,使用及模拟实现

              所以,若接着往下走,最后的输出结果就是

              1 2 1 2 1 2 1 2 1 2
              

              所以在使用 memcpy 函数时尽量不要 将 目标指针 和 源指针 指向同一块空间。


              – 函数的模拟实现

              #include 
              void* Sim_memcpy(void* dest, const void* src, size_t num)
              {
              	void* ret = dest;
              	assert(dest);
              	assert(src);
              	//先检测 dest 和 src 是否为空指针
              	while (num--) 
              	{
              		*(char*)dest = *(char*)src;// 非常精细,一个字节一个字节地复制
              		dest = (char*)dest + 1;
              		src = (char*)src + 1;
              	}
               return(ret);
              }
              

              2.memmove

              • 使用需包含头文件:

                – 函数原型

                void* memmove ( void* destination, const void* source, size_t num );
                                    目标空间				  源内容         拷贝数目
                

                memmove 函数的作用 和 memcpy 基本相同,是将一块空间(源)的前 num 个字节 的内容(注意!是字节不是字符个数),赋值粘贴到另一个空间(目标)。

                但是, memcpy 不能作用在 同一块空间, 也就是无法在空间重叠下复制,而 memmove 可以解决这个问题。

                规则:

                1. 源指针 和 目标指针 指向的对象的 基础类型 与此函数无关,即: memmove函数对任意类型的数据进行操作,例如字符串,数组和结构体等。
                2. memmove 函数 不会检测 源内容中是否存在结束标志 ’ \0 ',要求拷贝多少字节的内容就精确地拷贝多少字节的内容。
                3. memmove在拷贝时会有一个 缓冲区 来接受源空间,所以允许 源空间 和 目标空间 重叠。

                  4。 确保 目标空间 有足够的位置来存放复制的内容。


                – 函数使用

                第一种使用情况和 memmove 基本相同

                int dest_arr[10]; 
                int src_arr[] = { 1, 2, 3, 4, '\0', 5, 6, 7, 8, 9 };
                int sz = sizeof(src_arr) / sizeof(src_arr[0]);
                memmove(dest_arr, src_arr[], sz * sizeof(int));// 把整个源数组的内容都复制过去
                此时 dest_arr[10] 里面的内容就是 1, 2, 3, 4, 0, 5, 6, 7, 8, 9
                

                第二种—如果 源空间 和 目标空间 重叠时使用情况:

                int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10 };
                memmove(arr + 2, arr, 8 * sizeof(arr));
                for(int i= 0; i  
                

                结果是:

                1 2 1 2 3 4 5 6 7 8
                

                那为什么 memcpy 无法满足实现空间重叠的情况,而 memmove 却可以呢?

                【C语言】内存函数的概念,使用及模拟实现

                所以,在往后遇到 目标指针 和 源指针 指向的空间发生空间重叠时,可以使用 memmove 函数来解决。

                – 函数的模拟实现

                #include 
                void* Sim_memmove(void* dest, const void* src, size_t num)
                {
                 	void* ret = dest;
                ​	assert(dest);
                	assert(src);
                	//先检测 dest 和 src 是否为空指针
                  	if (dest = ((char*)src + num)) // 从前往后拷
                  	{
                    	while (num--) 
                    	{
                        	*(char*)dest = *(char*)src;
                        	dest = (char*)dest + 1;
                        	src = (char*)src + 1;
                 		}
                 	}
                  	else // 从后往前拷
                  	{
                    	dest = (char*)dest + count - 1;
                    	src = (char*)src + count - 1;
                    	while (numt--) 
                    	{
                        	*(char*)dest = *(char*)src;
                        	dest = (char*)dest - 1;
                        	src = (char*)src - 1;
                    	}
                    }
                  	return(ret);
                }
                

                有点小难理解,下面我们来看看:

                首先定义一个数组:int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
                

                第一种情况:

                Sim_memmove(arr + 2, arr, 5 * sizeof(int));
                

                `【C语言】内存函数的概念,使用及模拟实现

                第二种情况:

                Sim_memmove(arr, arr + 2, 5 * sizeof(int));
                

                【C语言】内存函数的概念,使用及模拟实现

                所以在模拟实现 memmove函数时,要确定拷贝顺序是 从前往后 还是 从后往前

                【C语言】内存函数的概念,使用及模拟实现


                3. memset

                • 使用需包含头文件:

                  – 函数原型

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

                  memset 函数用来将 ptr 指向的内存块的的指定范围(num个 字节)设置为指定值。

                  – 函数使用

                  char str[] = "Hello World";
                  memset(str, 'X', 5);
                  printf("%s", str);
                  

                  输出结果:

                  XXXXX World
                  

                  – 函数的模拟实现

                  #include 
                  void* Sim_memset(void* ptr, int value, size_t num)
                  {
                  	void* ret = ptr;
                  	assert(ptr != NULL);
                  	// 先检测 ptr 是否为空指针
                  	while (num--)
                  	{
                  		*(char*)ptr = value;
                  		(char*)ptr += 1;
                  	}
                  	return ret;
                  }
                  

                  4. memcmp

                  • 使用需包含头文件:

                    – 函数原型

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

                    memcmp 函数用来比较 ptr1 指向的内存块前 num个字节 的内容和 **ptr2 ** 指向的内存块的前 num个字节 的内容。

                    规则:

                    1. 该函数和 strcmp 相似,都是比较函数,但是 memcmp 函数在找到 ’ \0 ’ 字符后不会停止比较。
                    2. 该函数会返回一个整形值,该值指示的内存块的内容之间关系如下:
                    返回值表明
                    0在两个内存块中不匹配的第一个字节在 ptr1 中的值大于 ptr2 中的值

                    – 函数使用

                    比较两个数组

                    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
                    int arr2[] = { 1,2,3,4,5,6,7,8,9,11 };
                    int ret = memcmp(arr1, arr2, 10 * sizeof(int));
                    if (ret  arr2");
                    

                    输出结果:

                    arr1  
                    

                    比较两个字符串

                    char* str1 = "ABCDE";
                    char* str2 = "ABCDW";
                    int ret = memcmp(str1, str2, 10 * sizeof(char));
                    if (ret  arr2");
                    

                    输出结果

                    arr1  
                    

                    – 函数的模拟实现

                    int Sim_memcmp(const void* ptr1, const void* ptr2, size_t num)
                    {
                        assert(ptr1 != NULL);
                        assert(ptr2 != NULL);
                        // 先判断 ptr1 和 ptr2 是否为空指针
                        while ((*(char*)ptr1 == *(char*)ptr2) && num)
                        {
                            ((char*)ptr1)++;
                            ((char*)ptr2)++;
                        }
                        return *(char*)ptr1 - *(char*)ptr2;// 返回该字节内容的对应的 ASCII码表值的 差值
                    }
                    

                    该函数的模拟实现和 strcmp 的函数模拟实现有点相似,不同的是,strcmp 需要实现遇到

                    ’ \0 ’ 时便停止比较,而 memcmp 则是直接忽略 ’ \0 ’ 继续比较(字符若为 ’ \0 ’ 也会执行一次比较) 。

                      

                      

                      Stay hungry. Stay Foolish. 饥渴求知,虚怀若愚。

                      感谢各位读者支持,虚心请教,如有错漏或可改进点,请任意指出,感激不尽!

                      一起进步!


VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]