标题C语言对结构体使用memcmp
在软件开发中,经常会遇到对字符串,内存比较的处理,我们通常很少会自己手写一遍比较函数,会调用C语言库函数进行处理,如strcmp,strncmp,memcmp等。
面试的时候,面试官经常会问过类似这样一个题目,这个题目考的内容就是基础的字节对齐问题的扩展,觉得有必要写一下。
问题描述:
有如下结构体:
struct test
{
int member_a;
char member_b;
};
问,在32位机器上,sizeof(struct test) = ?
那毫无疑问,答案是8;
很典型的字节对齐问题,再比如将上诉结构体的两个成员变量上下两个数据类型互换之后,结果是多少?就是char在前,int在后。这都是一个类型的问题。这里不展开讲字节对齐问题,有很多大牛讲的已经很详细了。那这个问题的扩展问题是什么呢。如下:
struct test testA,和stauct test testB两个结构体,能否用memcmp比较这两个结构体是否相等,memcmp(testA, testB, sizeof(struct test));?
先不说结果,先写段代码测试一下:
#include <stdio.h>
#include <stdlib.h>
typedef struct Test
{
int member_a;
char member_b;
}test;
int main(int argc, char **argv)
{
test *test1 = (test*)malloc(sizeof(test));
if (!test1)
{
printf("alloc fail\n");
return -1;
}
test1->member_a = 0x12345678;
test1->member_b = 0x01;
test *test2 = (test*)malloc(sizeof(test));
if (!test2)
{
printf("alloc fail\n");
return -1;
}
test2->member_a = 0x12345678;
test2->member_b = 0x01;
int ret = memcmp(test1, test2, sizeof(test));
printf("ret = %d\n", ret);
free(test1);
test1 = NULL;
free(test2);
test2 = NULL;
return 0;
}
老样子,编译,执行:
ret=0;就说明这两个结构体相等,这样看起来没有什么问题。
那么我们回头来看,这个结构体是不是有效的字节数只有sizeof(int)+sizeof(char)=5,但是sizeof(struct test) = 8, 那么这相差的3个字节去哪了,我们知道malloc是不会对申请的内存进行初始化的,那么如果这三个字节之前的地址有别的内容的话,这里是不是就会出现问题?我们换一种思路,既然这个结构体有效部分只有5个字节,那么其他的三个字节是不是就可以任意处理了?操作一把,在申请到内存之后,把结构体初始化一下,稍作修改:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Test
{
int member_a;
char member_b;
}test;
int main(int argc, char **argv)
{
test *test1 = (test*)malloc(sizeof(test));
if (!test1)
{
printf("alloc fail\n");
return -1;
}
memset(test1, 0, sizeof(test)); //初始化全0
test1->member_a = 0x12345678;
test1->member_b = 0x01;
test *test2 = (test*)malloc(sizeof(test));
if (!test2)
{
printf("alloc fail\n");
return -1;
}
memset(test2, 1, sizeof(test)); //初始化全1
test2->member_a = 0x12345678;
test2->member_b = 0x01;
int ret = memcmp(test1, test2, sizeof(test));
printf("ret = %d\n", ret);
free(test1);
test1 = NULL;
free(test2);
test2 = NULL;
return 0;
}
编译、执行:
是不是就出问题了,但是这个结构体不影响使用,原有的成员变量还是可以正常操作的。只不过这里的memcmp就出问题了。
所以这个问题的答案就是,不能针对这个结构体进行memcmp比较是否相等,有风险。
那么问题又来了,如果我非要使用memcmp来比较呢,要怎么处理?
内心os:我看你是诚心想刁难我胖虎。
好吧,看下memcmp函数原型吧:
Man pages的关于memcmp的描述,针对开始的n个字节的内存进行比较,记中重点,要考的。上面我们是怎么传参数的, memcmp(test1, test2, sizeof(test)); 这里的第三个参数肯定是8啊,多了3个无关的字节啊。想个办法?当然不建议直接去搞他啊,那要换个结构体还得重新算一遍,万一算错了呢,万一不知道多少位的机器呢。还是保留这个sizeof吧。能不能对结构体操作一波?当然能。
不信你去百度搜一下结构体强制对齐,会出现一大把内容:
我选择其中一种方式,#pragma pack(1) //让编译器对这个结构体1字节对齐, #pragma pack() //取消强制对其。修改代码,如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(1)
typedef struct Test
{
int member_a;
char member_b;
}test;
#pragma pack()
int main(int argc, char **argv)
{
test *test1 = (test*)malloc(sizeof(test));
if (!test1)
{
printf("alloc fail\n");
return -1;
}
memset(test1, 0, sizeof(test)); //初始化全0
test1->member_a = 0x12345678;
test1->member_b = 0x01;
test *test2 = (test*)malloc(sizeof(test));
if (!test2)
{
printf("alloc fail\n");
return -1;
}
memset(test2, 1, sizeof(test)); //初始化全1
test2->member_a = 0x12345678;
test2->member_b = 0x01;
printf("sizeof(test) = %d\n", sizeof(test));
int ret = memcmp(test1, test2, sizeof(test));
printf("ret = %d\n", ret);
free(test1);
test1 = NULL;
free(test2);
test2 = NULL;
return 0;
}
编译运行:
完成,强制对齐的方式还有__attribute__,这里不展开讲了,小伙伴们可以自己去学习一下,溜了溜了。如果面试官再问你这个题目,知道怎么回答了吧。
attribute比较强大,只是再别人的代码中看见过,自己至今也是没用过,等抽空学习一下再来说这个吧。