memcmp 是C语言标准库中的一个函数,定义在
函数原型
int memcmp(const void *ptr1, const void *ptr2, size_t num);
功能:像法医比对指纹一样,逐字节比较两块内存区域的内容是否完全相同。
入口参数:鉴证科的三要素
- ptr1 - 第一块内存(现场样本)
- 类型:const void*
- 可以是任何数据类型:数组、结构体、字符串等
- 示例:int a[5] = {1,2,3,4,5}
- ptr2 - 第二块内存(比对样本)
- 类型:const void*
- 必须与ptr1类型一致才有比较意义
- 示例:int b[5] = {1,2,3,4,5}
- num - 比较的字节数(检测精度)
- 类型:size_t
- 关键设置:sizeof(数据类型)*元素个数
- 示例:比较5个int → 5*sizeof(int)
返回参数:鉴证报告
- 返回值=0:两块内存完全相同
char str1[] = "ABC";
char str2[] = "ABC";
int result = memcmp(str1, str2, 3); // 返回0
- 返回值<0:ptr1首个差异字节的ASCII值小于ptr2
// 0x41('A') vs 0x42('B')
memcmp("Apple", "Banana", 1); // 返回负数
- 返回值>0:ptr1首个差异字节的ASCII值大于ptr2
// 0x43('C') vs 0x42('B')
memcmp("Cat", "Bat", 1); // 返回正数
实战用法:三大鉴证场景
场景1:网络数据包校验(防数据篡改)
// 发送端生成校验码
struct Packet {
int seq;
char data[100];
uint32_t checksum; // 校验码
} send_pkt;
// 接收端验证校验码
uint32_t calc_checksum(const struct Packet *pkt) {
// 计算前两个字段的校验和(跳过checksum字段)
return crc32(pkt, offsetof(struct Packet, checksum));
}
void verify_packet(struct Packet *recv_pkt) {
uint32_t real_checksum = calc_checksum(recv_pkt);
// 比较内存中的校验码与计算的校验码(4字节)
if (memcmp(&recv_pkt->checksum, &real_checksum, 4) != 0) {
printf(" 数据包被篡改!");
}
}
场景2:文件签名验证(验明正身)
// PNG文件头签名:89 50 4E 47 0D 0A 1A 0A
int is_png_file(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp) return 0;
unsigned char header[8];
fread(header, 1, 8, fp);
fclose(fp);
// 比对前8字节是否符合PNG签名
return memcmp(header, "\x89PNG\r\n\x1A\n", 8) == 0;
}
/* 使用示例:
if (is_png_file("photo.png")) {
printf("这是合法的PNG文件");
} */
场景3:结构体快速比对(高效验证)
typedef struct {
int id;
char name[20];
double score;
} Student;
// 批量比对学生信息
void compare_students(const Student *s1, const Student *s2) {
// 直接比较整个结构体的内存(需确保无填充字节差异)
int cmp = memcmp(s1, s2, sizeof(Student));
if (cmp == 0) {
printf("学生信息完全相同\n");
} else {
printf("差异首次出现在第%d字节\n", abs(cmp));
}
}
// 注意:结构体可能存在内存对齐填充,需确保编译设置一致
高阶技巧:鉴证专家的工具箱
- 浮点数比较的陷阱
float a = 1.0f, b = 1.0f;
// 直接比较可能失败(因精度问题)
if (memcmp(&a, &b, sizeof(float)) == 0) {
printf("二进制表示的浮点数完全相同");
}
- 快速数组比对
int arr1[100], arr2[100];
// 比逐个元素比较快10倍以上
if (memcmp(arr1, arr2, sizeof(arr1)) == 0) {
printf("数组内容完全一致");
}
- 安全密码比对(防时序攻击)
// 使用恒定时间比较(即使差异在第一个字符也完整比对)
int safe_compare(const char *input, const char *real) {
size_t len = strlen(real);
if (strlen(input) != len) return -1;
int result = 0;
for (size_t i = 0; i < len; ++i) {
result |= input[i] ^ real[i]; // 逐位异或
}
return result; // 全等时返回0
}
注意事项:鉴证雷区
- 字节顺序问题(大小端差异)
// 0x12345678 在不同端序的存储:
// 大端:12 34 56 78
// 小端:78 56 34 12
int a = 0x12345678, b = 0x12345678;
memcmp(&a, &b, 4); // 在不同端序的机器上可能返回非0
- 结构体填充字节
#pragma pack(push, 1) // 关闭内存对齐
struct Test { char c; int i; };
#pragma pack(pop)
struct Test t1 = {'A', 100}, t2 = {'A', 100};
// 若编译时内存对齐设置不同,memcmp结果可能不同
- 浮点数的特殊值
double nan1 = 0.0/0.0, nan2 = 0.0/0.0;
memcmp(&nan1, &nan2, sizeof(double)); // 可能返回非0(NaN不相等)
总结:你的内存显微镜
对比项 | memcmp(内存比对) | strncmp(字符串比对) |
终止条件 | 比较指定字节数 | 遇到'\0'提前终止 |
适用类型 | 任意二进制数据 | 仅文本字符串 |
性能 | O(n) 时间复杂度 | O(n) 但可能提前终止 |
典型应用 | 数据校验、二进制协议 | 文件名比对、命令行参数解析 |
通过这份“内存鉴证指南”,你现在可以像专业鉴证人员一样使用memcmp函数,精准识别内存数据的异同!下次需要比对二进制数据时,记得启动你的内存显微镜!