嵌入式秋招面试中,一定要掌握的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。如果等于,那么说明最低地址存储的是最低有效字节,机器 是小端模式。如果不等于,那么说明最低地址存储的是最高有效字节,机器是大端模式。
完结,撒花。以上就是该期的全部内容。用图文并茂的方式给大家梳理了一下嵌入式面试中问的比较多的内存对齐以及大小端的问题。希望能够帮助到大家。文章若有错误,欢迎指出,积极改正!~
下期精彩内容敬请期待,谢谢大噶~
#嵌入式##嵌入式面试#