C语言是否能用memcmp函数比较结构体


今天继续分享自己看到的一个c语言小知识点,代码如下:

1.demo

实验环境:
Intel x86_64
ubuntu 20.04

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct test{
    
    
    short a;
    int b;
};

int main()
{
    
    
    struct test test_1 = {
    
    
        .a = 1,
        .b = 2,
    };

    struct test test_2 = {
    
    
        .a = 1,
        .b = 2,
    };

    if(memcmp(&test_1, &test_2, sizeof(struct test)) == 0){
    
    
        printf("test_1 == test_2\n");
    }else{
    
    
        printf("test_1 != test_2\n");
    }

    return 0;
}

上述结果相等吗?

2.memcmp函数

函数原型如下:

#include <string.h>

int memcmp(const void *s1, const void *s2, size_t n);

作用:比较内存区域s1和s2的前n个字节(每个字节都被解释为unsigned char)。

3.测试分析

结果显示不相等。
在这里插入图片描述
这是为什么了?

因为结构体的每一个成员都要能够自然对齐,如果没有对齐那就要进行填补。

对齐的好处:简化了处理器和内存系统之间接口的硬件设计,提高内存系统的性能。

x86_64架构 对齐的原则:任何K字节的基本对象的地址必须是K的倍数。
比如:一个short对象的起始地址一定是2的倍数,一个int对象的起始地址一定是4的倍数。

注意:我这里只是指x86_64架构下对齐原则。
在这里插入图片描述
由于要自然对齐,所以编译器要在short后面填充两个字节,填充的字节的值是多少呢?
那么我们写一个简单的例子来测试一下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct test{
    
    
    short a;
    int b;
};

int main()
{
    
    
    struct test test_1 = {
    
    
        .a = 11,
        .b = 22,
    };

    struct test test_2 = {
    
    
        .a = 11,
        .b = 22,
    };

    char *p1 = (char *)&test_1;
    for(int i = 0; i < sizeof(struct test); i++){
    
    
        printf("%10d ", *p1);
        p1++;
    }
    printf("\n");

    char *p2 = (char *)&test_2;
    for(int i = 0; i < sizeof(struct test); i++){
    
    
        printf("%10d ", *p2);
        p2++;
    }

    printf("\n");

    return 0;
}

结果如下:
在这里插入图片描述

我测试多次,由于是局部变量,局部变量如果没有初始化,那么程序运行中调用该函数时会给其初始化为一个随机值,所以填充的字节值也是随机的,那么填充的这两个字节的值大小不确定,那我们调用memcpy进行结构体比较时,每个字节都要进行比较,结果当然就不相等。

修改方案1:
当我把第一个成员由short改为int后,结构体已经自然对齐了,不用再填充字节,所以这次比较就会相等。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct test{
    
    
    int a;
    int b;
};

int main()
{
    
    
    struct test test_1 = {
    
    
        .a = 1,
        .b = 2,
    };

    struct test test_2 = {
    
    
        .a = 1,
        .b = 2,
    };

    if(memcmp(&test_1, &test_2, sizeof(struct test)) == 0){
    
    
        printf("test_1 == test_2\n");
    }else{
    
    
        printf("test_1 != test_2\n");
    }

    return 0;
}

在这里插入图片描述
修改方案2:
将 struct test test_1 和 struct test test_2结构体改为全局变量:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct test{
    
    
    short a;
    int b;
};

struct test test_1 = {
    
    
    .a = 11,
    .b = 22,
};

struct test test_2 = {
    
    
    .a = 11,
    .b = 22,
};

int main()
{
    
    

    if(memcmp(&test_1, &test_2, sizeof(struct test)) == 0){
    
    
        printf("test_1 == test_2\n");
    }else{
    
    
        printf("test_1 != test_2\n");
    }

    char *p1 = (char *)&test_1;
    for(int i = 0; i < sizeof(struct test); i++){
    
    
        printf("%-10d ", *p1);
        p1++;
    }
    printf("\n");

    char *p2 = (char *)&test_2;
    for(int i = 0; i < sizeof(struct test); i++){
    
    
        printf("%-10d ", *p2);
        p2++;
    }

    printf("\n");

    return 0;
}

在这里插入图片描述
当把 struct test test_1 和 struct test test_2结构体改为全局变量后,由于Linux c中规定未初始化的全局变量,运行时会将其初始化为0,所以填充的两个字节的值也是0,那么这种情况下就可以用 memcmp 比较了。

linux系统中:未初始化的全局变量属于可执行文件(ELF格式)的bss段,bss段不占用磁盘空间,仅仅是一个占位符,这样可以节省磁盘空间大小。在运行时,系统将bss段全部初始化为0。

结论

最好不要用memcmp函数来比较结构体的大小。
言外之意:最好不要通过一个一个字节的方式来比较结构体的大小,因为体系架构对齐要填充字节的缘故,很有可能会填充垃圾信息在结构体中。

参考资料

深入理解计算机系统
Linux环境编程从应用到内核

猜你喜欢

转载自blog.csdn.net/weixin_45030965/article/details/124279645