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

本文阅读 4 分钟
首页 Linux,系统 正文

今天继续分享自己看到的一个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.测试分析

结果显示不相等。 img 这是为什么了?

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

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

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

注意:我这里只是指x86_64架构下对齐原则。 img 由于要自然对齐,所以编译器要在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;
}

结果如下: img

我测试多次,由于是局部变量,局部变量如果没有初始化,那么程序运行中调用该函数时会给其初始化为一个随机值,所以填充的字节值也是随机的,那么填充的这两个字节的值大小不确定,那我们调用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;
}

img 修改方案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;
}

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

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

结论

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

参考资料

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

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/weixin_45030965/article/details/124279645
-- 展开阅读全文 --
BUUCTF Web [极客大挑战 2019]Knife
« 上一篇 06-24
安全面试之XSS(跨站脚本攻击)
下一篇 » 07-24

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复