【C标准库】深入理解C语言memcmp函数:内存比较的利器


🌈个人主页:聆风吟_
🔥系列专栏:C语言标准库
🔖少年有梦不应止于心动,更要付诸行动。


文章目录

📋前言

在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) 逐字节比较,返回值分三种情况:

  1. 返回 0str1str2n 个字节完全相等
  2. 返回 小于0的值str1n 个字节 小于 str2
  3. 返回 大于0的值str1n 个字节 大于 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) 或逐字段比较。


📝全文总结

  1. memcmp 是C语言标准库中逐字节比较内存的函数,功能强大且通用
  2. 核心:#include <string.h>,返回0表示内存相等
  3. 区分 strcmpmemcmp 不关心结束符,比较任意内存
  4. 开发中用于结构体、数组、二进制数据比较,是必备工具函数

但使用时必须时刻警惕结构体填充、指针成员等带来的陷阱。对于大多数应用场景,memcmp 是正确且高效的选择;而在处理复杂数据结构时,编写专门的比较函数往往更可靠。


希望本文能帮助你更好地理解和使用 memcmp。如有疑问或发现错误,欢迎讨论交流!

相关推荐
艾莉丝努力练剑2 小时前
【Linux线程】Linux系统多线程(一):线程概念
java·linux·运维·服务器·开发语言·学习·线程
低保和光头哪个先来2 小时前
Axios 近期安全版本
开发语言·前端·javascript·前端框架
zzwq.2 小时前
深入理解Python闭包与装饰器:从入门到进阶
开发语言·python
嵌入式学习菌2 小时前
内网穿透全闭环实操指南
linux·开发语言·php
Yupureki2 小时前
《Linux网络编程》2.Socket编程(UDP/TCP)
linux·服务器·c语言·网络·c++·tcp/ip·udp
两年半的个人练习生^_^2 小时前
ThreadLocal的使用和源码
java·开发语言
csbysj20202 小时前
JSP 语法详解
开发语言
roamingcode2 小时前
应对 Codex 0.118.0 破坏性更新:Slash Prompt Router 架构解析与实践
java·开发语言·prompt·codex·skill
zzginfo2 小时前
JavaScript 假值示例详解
开发语言·前端·javascript·ecmascript