
🌈个人主页:聆风吟_
🔥系列专栏:C语言标准库
🔖少年有梦不应止于心动,更要付诸行动。
文章目录
- 📋前言
- 一、memcmp函数基础
-
- [1.1 函数原型](#1.1 函数原型)
- [1.2 核心作用](#1.2 核心作用)
- [1.3 参数说明](#1.3 参数说明)
- [1.4 返回值规则](#1.4 返回值规则)
- 二、memcmp与strcmp的核心区别
- 三、实用代码示例
- 四、memcmp与strcmp的区别
- 五、memcmp底层实现原理
- 六、常见应用场景
-
- [6.1 比较结构体(谨慎使用)](#6.1 比较结构体(谨慎使用))
- [6.2 验证数据完整性](#6.2 验证数据完整性)
- [6.3 高效数组比较](#6.3 高效数组比较)
- 七、常见陷阱与最佳实践
-
- [❌ 陷阱1:比较字符串时忘记 `n` 应为字节数](#❌ 陷阱1:比较字符串时忘记
n应为字节数) - [❌ 陷阱2:比较含指针的结构体](#❌ 陷阱2:比较含指针的结构体)
- [❌ 陷阱3:忽略结构体填充字节](#❌ 陷阱3:忽略结构体填充字节)
- [❌ 陷阱1:比较字符串时忘记 `n` 应为字节数](#❌ 陷阱1:比较字符串时忘记
- 📝全文总结
📋前言
在C语言开发中,我们经常需要对内存块 进行比较操作。strcmp函数只能用于比较字符串(遇到\0停止),而对于任意二进制数据、结构体、数组等非字符串类型的内存比较,memcmp就是最实用的工具。
本篇博客将带你全面掌握memcmp的用法、原理、示例和注意事项,让你在开发中轻松搞定内存比较。
一、memcmp函数基础
1.1 函数原型
c
#include <string.h>
int memcmp(const void *str1, const void *str2, size_t n);
1.2 核心作用
逐字节比较两块内存区域的前 n 个字节,不受数据类型、结束符限制,纯粹按字节值比较。
1.3 参数说明
| 参数 | 含义 |
|---|---|
str1 |
第一个内存区域的指针(const保证不会修改原数据) |
str2 |
第二个内存区域的指针 |
n |
需要比较的字节数(无符号整数) |
1.4 返回值规则
memcmp 按照无符号字符(unsigned char) 逐字节比较,返回值分三种情况:
- 返回 0 :
str1和str2前n个字节完全相等 - 返回 小于0的值 :
str1前n个字节 小于str2 - 返回 大于0的值 :
str1前n个字节 大于str2
二、memcmp与strcmp的核心区别
这是面试和开发中最容易混淆的点,一定要记牢:
| 函数 | 比较对象 | 终止条件 | 适用场景 |
|---|---|---|---|
strcmp |
字符串 | 遇到 \0 停止 |
仅字符串比较 |
memcmp |
任意内存 | 比较完 n 个字节停止 |
所有类型内存数据 |
✅ 关键总结:
- 比较字符串用
strcmp - 比较结构体、数组、二进制数据用
memcmp
三、实用代码示例
示例1:基础用法(比较字节数组)
c
#include <stdio.h>
#include <string.h>
int main()
{
// 定义两个字节数组
unsigned char arr1[] = { 0x01, 0x02, 0x03, 0x04 };
unsigned char arr2[] = { 0x01, 0x02, 0x03, 0x05 };
// 比较前4个字节
int ret = memcmp(arr1, arr2, 4);
if (ret == 0)
{
printf("两块内存完全相等\n");
}
else if (ret < 0)
{
printf("arr1 < arr2\n");
}
else
{
printf("arr1 > arr2\n");
}
return 0;
}
输出结果:
arr1 < arr2
示例2:比较结构体(开发常用)
c
#include <stdio.h>
#include <string.h>
// 定义结构体
struct Student
{
int id;
char name[20];
float score;
};
int main()
{
struct Student stu1 = { 1001, "张三", 95.5 };
struct Student stu2 = { 1001, "张三", 95.5 };
// 比较整个结构体的内存
int ret = memcmp(&stu1, &stu2, sizeof(struct Student));
if (ret == 0)
{
printf("两个学生信息完全一致\n");
}
else
{
printf("两个学生信息不一致\n");
}
return 0;
}
输出结果:
两个学生信息完全一致
示例3:包含\0的内存比较
c
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "hello\0world";
char str2[] = "hello\0c-lang";
// 比较全部12字节
int ret = memcmp(str1, str2, 12);
if (ret == 0)
{
printf("内存完全相同\n");
}
else
{
printf("内存不同\n");
}
return 0;
}
输出结果:
内存不同
👉 对比:strcmp 遇到 \0 就会停止比较,而 memcmp 会继续比较后续字节。
四、memcmp与strcmp的区别
| 特性 | memcmp |
strcmp |
|---|---|---|
| 比较长度 | 固定字节数 n |
直到遇到 \0 |
| 二进制安全 | ✅ 支持含零字节的数据 | ❌ 遇到 \0 即停 |
| 比较单元 | 字节 | 字节(但按字符语义) |
| 效率 | 通常更高(长度已知) | 需扫描查找 \0 |
c
// 示例:包含零字节的数据
char data1[] = { 'A', 'B', '\0', 'C' };
char data2[] = { 'A', 'B', '\0', 'D' };
// memcmp 可以正确比较全部4字节
int r1 = memcmp(data1, data2, 4); // 返回负数('C' < 'D')
// strcmp 在遇到 '\0' 时停止,认为相等
int r2 = strcmp(data1, data2); // 返回 0
五、memcmp底层实现原理
理解memcmp的典型实现有助于掌握其行为特性:
c
int my_memcmp(const void* s1, const void* s2, size_t n)
{
const unsigned char* p1 = (const unsigned char*)s1;
const unsigned char* p2 = (const unsigned char*)s2;
for (size_t i = 0; i < n; i++)
{
if (p1[i] != p2[i])
{
return (p1[i] > p2[i]) ? 1 : -1;
}
}
return 0;
}
关键点:
- 使用
unsigned char*而非char*,确保比较时使用无符号字节值(0-255) - 严格按字节顺序比较,遇到第一个差异即返回
- 不会因遇到
\0而提前停止(与strcmp的关键区别)
六、常见应用场景
6.1 比较结构体(谨慎使用)
c
typedef struct
{
int id;
char name[20];
float score;
} Student;
// 仅当结构体不含指针且内存对齐不影响时可直接比较
Student s1 = { 1001, "Alice", 95.5 };
Student s2 = { 1001, "Alice", 95.5 };
// 直接内存比较(有潜在风险)
if (memcmp(&s1, &s2, sizeof(Student)) == 0)
{
printf("结构体内容相同\n");
}
⚠️ 警告 :结构体可能有内存对齐产生的填充字节(padding),其值未定义,可能导致本应相等的两个结构体被判断为不等。建议为结构体定义专门的比较函数。
6.2 验证数据完整性
c
#include <stdint.h>
uint32_t calculate_checksum(void* data, size_t len)
{
uint32_t sum = 0;
unsigned char* p = (unsigned char*)data;
for (size_t i = 0; i < len; i++)
{
sum += p[i];
}
return sum;
}
// 发送端:发送数据和校验和
// 接收端:重新计算校验和并与接收到的校验和比较
6.3 高效数组比较
c
int compare_int_arrays(int a[], int b[], size_t count)
{
return memcmp(a, b, count * sizeof(int));
}
七、常见陷阱与最佳实践
❌ 陷阱1:比较字符串时忘记 n 应为字节数
c
char *str1 = "Hello";
char *str2 = "Hello";
// 错误:比较的是指针本身,不是字符串内容
if (memcmp(&str1, &str2, sizeof(char*)) == 0) { ... }
// 正确:比较字符串内容
if (memcmp(str1, str2, 6) == 0) { ... } // 6 包括 '\0'
❌ 陷阱2:比较含指针的结构体
c
typedef struct
{
char *name; // 指针指向堆内存
int age;
} Person;
Person p1 = {malloc(10), 20};
Person p2 = {malloc(10), 20};
strcpy(p1.name, "Tom");
strcpy(p2.name, "Tom");
// 错误:比较的是指针地址,而非 name 指向的内容
if (memcmp(&p1, &p2, sizeof(Person)) == 0) { ... } // 结果不确定
// 正确:逐字段比较
int cmp = (p1.age == p2.age) && (strcmp(p1.name, p2.name) == 0);
❌ 陷阱3:忽略结构体填充字节
c
typedef struct
{
char c; // 1字节
int i; // 4字节,会存在 3 字节 padding
} Packed;
Packed a = {'A', 100};
Packed b = {'A', 100};
// a 和 b 的逻辑值相同,但 padding 字节可能是随机值
if (memcmp(&a, &b, sizeof(Packed)) == 0) { ... } // 可能失败
解决方案 :使用 #pragma pack(1) 或逐字段比较。
📝全文总结
memcmp是C语言标准库中逐字节比较内存的函数,功能强大且通用- 核心:
#include <string.h>,返回0表示内存相等 - 区分
strcmp:memcmp不关心结束符,比较任意内存 - 开发中用于结构体、数组、二进制数据比较,是必备工具函数
但使用时必须时刻警惕结构体填充、指针成员等带来的陷阱。对于大多数应用场景,memcmp 是正确且高效的选择;而在处理复杂数据结构时,编写专门的比较函数往往更可靠。
希望本文能帮助你更好地理解和使用 memcmp。如有疑问或发现错误,欢迎讨论交流!