嵌入式秋招面试中,一定要掌握的c基础—内存对齐、大小端(2)

哈喽,大家好,这里是自律鸽。正如标题所强调的,在嵌入式秋招面试中,被问到c语言相关知识的概率几乎达到了百分之百。我本人也在去年的秋招中深切体验了这一点。因此,我想与大家分享一些面试中常问的c基础,这些也都是我在求职过程中积累的宝贵经验。

内存对齐

1、为什么要进行内存对齐?

在嵌入式面试中,常常会被问到为什么要进行内存对齐?其实往深了讲有很多原因,但只要你讲出如下关键的几点就可以了。

(1)硬件平台限制

●  内存以字节为单位,但不同硬件平台不一定支持任何内存地址的存取。例如,某些硬件平台可能以双字节、4字节等为单位存取内存。因此,为了保证处理器正确读取数据,需要进行内存对齐。

(2)提高cpu内存访问速度

●  处理器访问内存时,对齐的数据可以在较少的内存访问周期内被读取或写入。

●  如果数据没有对齐,处理器可能需要进行额外的处理来组合不同内存地址中的数据片段,这会增加处理器的工作负担,降低效率。

(3)减少内存碎片

●  通过内存对齐,可以减少因小块内存分配造成的内存碎片,从而提高内存的使用效率。

2、举例说明(常常以结构体对齐为考题)

不同系统下数据类型所占的字节数大小

示例1

#include <stdio.h>  
#include <stddef.h> // for offsetof  
// 定义一个结构体,成员变量包括char、short、int、char[]  
struct Struct1 {  
    char c;  
    short s;  
    int i;  
    char arr[10];  
}; 
int main() {  
    // 打印第一个结构体的大小  
    printf("Size of Struct1: %zu bytes\n", sizeof(struct Struct1));    
    // 可以打印出结构体中每个成员变量的偏移量 
    printf("Offset of c in Struct1: %zu bytes\n", offsetof(struct Struct1, c));  
    printf("Offset of s in Struct1: %zu bytes\n", offsetof(struct Struct1, s));  
    printf("Offset of i in Struct1: %zu bytes\n", offsetof(struct Struct1, i));  
    printf("Offset of arr in Struct1: %zu bytes\n", offsetof(struct Struct1, arr));  
    return 0;  
}

上述代码中结构体所占字节数为20bytes,各变量偏移量分别为0,2,4,8bytes。那么该结构体所占的字节数是如何计算的呢?

由代码可知,结构体Struct1中最宽的数据类型为int整型,所占字节数为4。因此,在计算各变量所占字节数时,应该以4为最大字节数数划分。比如char c和short s所占字节数为3,小于4即需要填充后再偏移。

示例2

#include <stdio.h>  
#include <stddef.h> // for offsetof  
// 定义一个结构体,成员变量包括char、short、int、char[]  
struct Struct1 {  
    char c;  
    short s;  
    int i; 
    double d; 
    char arr[10];  
}; 
int main() {  
    // 打印第一个结构体的大小  
    printf("Size of Struct1: %zu bytes\n", sizeof(struct Struct1));    
    // 可以打印出结构体中每个成员变量的偏移量 
    printf("Offset of c in Struct1: %zu bytes\n", offsetof(struct Struct1, c));  
    printf("Offset of s in Struct1: %zu bytes\n", offsetof(struct Struct1, s));  
    printf("Offset of i in Struct1: %zu bytes\n", offsetof(struct Struct1, i)); 
    printf("Offset of d in Struct1: %zu bytes\n", offsetof(struct Struct1, d));   
    printf("Offset of arr in Struct1: %zu bytes\n", offsetof(struct Struct1, arr));  
    return 0;  
}

上述代码中结构体所占字节数为32bytes,各变量偏移量分别为0,2,4,8,16bytes。

由代码可知,结构体Struct1中最宽的数据类型为double双精度型,所占字节数为8。因此,在计算各变量所占字节数时,应该以8为最大字节数数划分。比如char c和short s、int i所占字节数为7,小于8即需要填充后再偏移到下一个内存块。

总结一下:计算结构体时应遵循的几大原则

(1)结构体的首地址能够被其最宽基本类型成员的大小所整除;

(2)结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;

(3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节;

题外话:如何计算联合体union和枚举enum所占字节数大小呢?

联合体union

#include <stdio.h>  
  
// 定义一个包含 long, int, char 的 union  
union MyUnion {  
    long l;  
    int i[5];  
    char c;  
};  
  
int main() {  
    // 使用 sizeof 运算符获取 union 的大小  
    size_t unionSize = sizeof(union MyUnion);  
  
    // 打印 union 的大小  
    printf("Size of union MyUnion: %zu bytes\n", unionSize);   
  
    return 0;  
}

在c语言中,union联合体是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。但是,union的大小至少足够大以容纳其最大的成员。而在该代码中数组i为最大的成员。因此,union联合体所占字节数为20bytes。

枚举enum

#include <stdio.h>  
  
// 定义一个枚举  
enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };  
  
int main() {  
    // 使用sizeof运算符获取枚举变量的大小  
    size_t enumSize = sizeof(enum Days);  
  
    // 打印枚举变量的大小  
    printf("Size of enum Days variable: %zu bytes\n", enumSize);  
  
    // 通常,枚举变量的大小与int相同,所以也打印int的大小以作比较  
    printf("Size of int: %zu bytes\n", sizeof(int));  
  
    return 0;  
}

一般情况而言,枚举enum所占的字节大小跟int类型相同。但当成员变量大小超过int类型所占字节大小时,枚举enum所占的字节大小为8,跟long long类型相同。

tips:上述实验环境均为32位系统。

判断大小端方法

1、什么是大小端?

在c语言中,判断机器的字节序(大端模式还是小端模式)通常是通过检查一个整数的字节在内存中的存储顺序来实现的。大端模式(Big-Endian)中,最高有效字节(MSB)存储在最低的内存地址处,而小端模式(Little-Endian)中,最低有效字节(LSB)存储在最低的内存地址处。

2、大小端判断方法

简单示例,易理解:

#include <stdio.h>  
  
// 判断机器大小端  
int check_endian() {  
    unsigned int x = 0x12345678;  
    char *c = (char *)&x;  
  
    // 如果最低地址处存储的是最低有效字节,则为小端  
    // 如果最低地址处存储的是最高有效字节,则为大端  
    if (*c == 0x78) {  
        return 1; // 小端  
    } else {  
        return 0; // 大端  
    }  
}  
  
int main() {  
    if (check_endian()) {  
        printf("Little-Endian\n");  
    } else {  
        printf("Big-Endian\n");  
    }  
  
    return 0;  
}

在这个示例中,我们定义了一个无符号整数x,并给它赋了一个十六进制值0x12345678。然后,我们定义了一个字符指针c,并将其指向x的地址。实则c指向的是x的最低字节。因此,我们检查*c(即x的最低字节)是否等于0x78。如果等于,那么说明最低地址存储的是最低有效字节,机器 是小端模式。如果不等于,那么说明最低地址存储的是最高有效字节,机器是大端模式。

完结,撒花。以上就是该期的全部内容。用图文并茂的方式给大家梳理了一下嵌入式面试中问的比较多的内存对齐以及大小端的问题。希望能够帮助到大家。文章若有错误,欢迎指出,积极改正!~

下期精彩内容敬请期待,谢谢大噶~

#嵌入式##嵌入式面试#
全部评论

相关推荐

前两天有个明年6月份研究生毕业的同学来找我看简历。她说自己本科、研究生基本都在学校里,没做过什么实习。现在要找工作了,感觉简历很单薄,问我有什么方法能够让她的简历看上去更丰富一点。我看了她的简历,其实蛮丰富的。虽然没有在公司里实习的经历,但也组织过一些学校的活动,担任过班级和学生会的一些职务,其实没她自己说得那么单薄。她问我:“但我听说公司招人很看重实习经历,像我这样的简历是不是找工作很困难?”我说:“公司招应届生确实挺看重实习经历的。如果你之前的实习经历和你现在想应聘的岗位是相关的,公司就会觉得你对我们这个岗位是有概念的,有一些基础,不必从零开始教你。所以有实习经历确实是个比较大的优势。”她说:“那我怎么办啊?啥实习都没有!”我说:“你距离明年毕业还有9个月的时间,从零开始积累实习经验都来得及!一般一份实习做3个月,9个月你都可以做3份实习了。现在开始做真的不晚!”我经常建议毕业以后想要进入企业工作的同学在大学期间多做实习。做实习的真正目的其实不是为了让你的简历更好看,而是在这个过程中探索自己究竟对什么样的工作感兴趣、可以接受什么样的工作状态。学生身份的试错成本是很低的,利用好这段时间多多尝试。虽然不太可能一下子就找到那份愿意干一辈子的工作,但至少能通过更多的尝试,找到自己相对能接受的那份工作。虽然几乎每一份工作都有令人痛苦的时刻,但我相信没有人不想让自己在工作中能够开心一点。“用最小的成本多多进行尝试”就是能够让你的第一份工作相对开心一点最好的办法! #牛客创作赏金赛#
点赞 评论 收藏
分享
3 12 评论
分享
牛客网
牛客企业服务