C语言内存函数详解:memcpy、memmove、memset、memcmp 面试复习指南
目录 1. 引言 2. memcpy 函数内存拷贝的基础2.1 函数原型2.2 功能说明2.3 使用示例2.4 模拟实现 3. memmove 函数处理重叠内存的拷贝3.1 函数原型3.2 功能说明3.3 使用示例3.4 模拟实现 4. memset 函数内存设置4.1 函数原型4.2 功能说明4.3 使用示例⚖️ 5. memcmp 函数内存比较5.1 函数原型5.2 功能说明5.3 使用示例 6. 总结 7. 经典面试题 1. 引言大家好欢迎来到C语言内存函数的专题讲解。在C语言编程中内存操作是极其重要的一环尤其是在嵌入式开发、系统编程和面试中memcpy、memmove、memset和memcmp这四个内存函数几乎是必考知识点。本文将带你从函数原型、使用示例、模拟实现到面试常见问题全方位掌握这些内存函数。文章内容基于经典教材和实际开发经验代码完整可运行非常适合面试前的系统复习。 2. memcpy 函数内存拷贝的基础2.1 函数原型void*memcpy(void*destination,constvoid*source,size_tnum);2.2 功能说明memcpy从source位置开始向后复制num个字节的数据到destination指向的内存位置。这个函数在遇到\0的时候并不会停下来它只关心num指定的字节数。⚠️重要限制如果source和destination有任何的重叠复制的结果都是未定义的。对于重叠的内存应该交给memmove来处理。2.3 使用示例#includestdio.h#includestring.hintmain(){intarr1[]{1,2,3,4,5,6,7,8,9,10};intarr2[10]{0};// 将 arr1 的前 20 个字节5个int复制到 arr2memcpy(arr2,arr1,20);inti0;for(i0;i10;i){printf(%d ,arr2[i]);}// 输出结果1 2 3 4 5 0 0 0 0 0return0;}2.4 模拟实现void*memcpy(void*dst,constvoid*src,size_tcount){void*retdst;assert(dst);assert(src);/* * copy from lower addresses to higher addresses */while(count--){*(char*)dst*(char*)src;dst(char*)dst1;src(char*)src1;}return(ret);}实现要点使用char *指针逐字节拷贝因为char类型占1个字节可以精确控制拷贝的字节数。先保存dst的原始地址作为返回值方便链式调用。使用assert进行空指针检查增强代码健壮性。 3. memmove 函数处理重叠内存的拷贝3.1 函数原型void*memmove(void*destination,constvoid*source,size_tnum);3.2 功能说明和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠就得使用memmove函数处理。3.3 使用示例#includestdio.h#includestring.hintmain(){intarr1[]{1,2,3,4,5,6,7,8,9,10};// 将 arr1 的前 20 个字节复制到 arr12 的位置重叠场景memmove(arr12,arr1,20);inti0;for(i0;i10;i){printf(%d ,arr1[i]);}// 输出结果1 2 1 2 3 4 5 8 9 10return0;}输出结果1 2 1 2 3 4 5 8 9 103.4 模拟实现void*memmove(void*dst,constvoid*src,size_tcount){void*retdst;if(dstsrc||(char*)dst((char*)srccount)){/* * Non-Overlapping Buffers * copy from lower addresses to higher addresses */while(count--){*(char*)dst*(char*)src;dst(char*)dst1;src(char*)src1;}}else{/* * Overlapping Buffers * copy from higher addresses to lower addresses */dst(char*)dstcount-1;src(char*)srccount-1;while(count--){*(char*)dst*(char*)src;dst(char*)dst-1;src(char*)src-1;}}return(ret);}实现要点核心思想是判断内存是否重叠以及从哪个方向开始拷贝。当dst src或dst src count时说明没有重叠或目标地址在源地址之前可以从低地址向高地址拷贝。当存在重叠且目标地址在源地址之后时必须从高地址向低地址拷贝避免覆盖未拷贝的数据。 4. memset 函数内存设置4.1 函数原型void*memset(void*ptr,intvalue,size_tnum);4.2 功能说明memset是用来设置内存的将内存中的值以字节为单位设置成想要的内容。4.3 使用示例#includestdio.h#includestring.hintmain(){charstr[]hello world;memset(str,x,6);printf(str);// 输出结果xxxxxxworldreturn0;}输出结果xxxxxxworld注意事项memset以字节为单位操作对于非字符数组如int数组设置的值会被解释为字节值。例如memset(arr, 1, 20)会将每个字节设为0x01而不是将每个int设为1。常用于初始化数组为0memset(arr, 0, sizeof(arr))。⚖️ 5. memcmp 函数内存比较5.1 函数原型intmemcmp(constvoid*ptr1,constvoid*ptr2,size_tnum);5.2 功能说明比较从ptr1和ptr2指针指向的位置开始向后的num个字节。返回值返回值 0ptr1小于ptr2返回值 0ptr1等于ptr2返回值 0ptr1大于ptr25.3 使用示例#includestdio.h#includestring.hintmain(){charbuffer1[]DWgaOtP12df0;charbuffer2[]DWGAOTP12DF0;intn;nmemcmp(buffer1,buffer2,sizeof(buffer1));if(n0)printf(%s is greater than %s.\n,buffer1,buffer2);elseif(n0)printf(%s is less than %s.\n,buffer1,buffer2);elseprintf(%s is the same as %s.\n,buffer1,buffer2);return0;}与strcmp的区别strcmp遇到\0就停止比较而memcmp比较指定的num个字节。memcmp可以比较任意内存块包括包含\0的数据而strcmp只能比较字符串。 6. 总结函数功能处理重叠内存停止条件memcpy内存拷贝❌ 未定义指定字节数memmove内存拷贝✅ 安全处理指定字节数memset内存设置—指定字节数memcmp内存比较—指定字节数核心记忆口诀memcpy不重叠直接拷memmove重叠了分方向memset设内存按字节memcmp比内存定字节 7. 经典面试题面试题 1memcpy和memmove有什么区别什么时候用memmove解答memcpy不保证能正确处理源和目标内存重叠的情况如果内存重叠结果是未定义的。memmove可以安全处理重叠内存。当源内存块和目标内存块有重叠时必须使用memmove否则两者都可以使用但memcpy通常效率略高。面试题 2memcpy和strcpy有什么区别解答strcpy只能用于字符串拷贝遇到\0停止memcpy可以拷贝任意类型的内存数据。strcpy不指定拷贝长度memcpy需要指定拷贝的字节数。memcpy可以拷贝包含\0的数据块而strcpy遇到\0就停止。面试题 3memset(arr, 1, sizeof(arr))能把 int 数组的每个元素设为 1 吗解答不能。memset以字节为单位设置内存int占4个字节每个字节都会被设为0x01所以每个int的值实际上是0x01010101十六进制即16843009。正确做法是使用循环或memset(arr, 0, sizeof(arr))来清零数组。面试题 4请手写一个memmove的实现并说明为什么需要分方向拷贝解答参考上文第3.4节的模拟实现。分方向拷贝的原因当源地址和目标地址重叠且目标地址在源地址之后时如果从低地址向高地址拷贝会先覆盖源数据中尚未拷贝的部分导致数据错误。从高地址向低地址拷贝可以避免这个问题。面试题 5memcmp和strcmp的返回值规则一样吗比较结果有什么不同解答返回值规则相同都返回0、0、0。不同点strcmp遇到\0停止比较而memcmp比较完指定的num个字节才停止。因此对于包含\0的二进制数据只能用memcmp进行比较。面试小贴士面试官通常会让手写memcpy或memmove的模拟实现重点考察对内存重叠的处理、指针操作和边界条件的把握。建议在面试前多练习几遍手写代码做到烂熟于心。
