`
lobin
  • 浏览: 417522 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

C 第1000章:内存对齐

 
阅读更多

先从一个简单的结构体开始:

struct st 

{

  int a;

  int b;

  int c;

};

这个结构体大小是多少?

 

在gcc中,整型int占4个字节。所以结构体struct st的大小为4+4+4=12个字节。这个结构体大小的确是12个字节,我们可以通过sizeof(struct st)获知这个结构体的大小。

 

struct st 

{

  char a;

  int b;

  int c;

};

那么这个呢?

 

在gcc中,整型char占1个字节。按照上面那样简单的将各个成员所需占用的内存大小相加,那么应该是1+4+4=9个字节。但实际上还是12个字节。通过sizeof(struct st)得到的这个结构体的大小也是12个字节。

 

为什么?

 

结构体包含多个成员,给结构体变量分配内存其实就是给结构体中每个成员分配内存。在给结构体变量分配内存的时候,通常根据各成员所需占用内存大小分配内存空间,第1个成员从结构体开始偏移0处分配内存,第2个成员紧随第1个成员分配内存,也就是从结构体开始偏移第1个成员大小的位置分配内存,或者说在第1个成员的偏移位置加上第1个成员大小的位置分配内存,后面每个成员都是紧随前一个成员分配内存,或者说在前1个成员的偏移位置加上前1个成员大小的位置分配内存。正常来说,结构体大小为各成员大小之和。

 

所以上面第1个结构体struct st的大小为12字节。

 

但编译器会对这个结构体到底需要分配多大的内存进行一项优化。这个优化就是内存对齐。

 

因为对齐需要,所以在给结构体struct st分配内存的时候,也就是给各个成员分配内存,并不是紧挨a给b分配内存空间,在a和b之间扩充了3个字节,也就是存在3个字节的”空洞“。

 

我们可以看看在给结构体struct st变量分配内存时的内存布局是怎样的。

 

也就是在内存中是怎么给结构体struct st变量分配内存的:

写道
+----+ -
| | a: 1byte
+----+ -
| | ^
+----+ |
| | 3bytes holes
+----+ |
| | v
+----+ -
| | ^
+----+ |
| | |
+----+ b: 4bytes
| | |
+----+ |
| | v
+----+ -
| | ^
+----+ |
| | |
+----+ c: 4bytes
| | |
+----+ |
| | v
+----+ -

也就是成员a在结构体struct st变量起始偏移0处开始分配内存,分配1个字节,在给成员b分配内存的时候,因为对齐需要,需要偏移3个字节,到第4个字节开始给成员b分配内存。

 

为什么是偏移3个字节,到第4个字节开始给成员b分配内存?

 

对齐基数

也叫对齐系数,其实应该叫最大结构体成员对齐(maximum structure member alignment)。

 

这个基数在编译的时候可以通过-fpack-struct指定

 

-fpack-struct               Pack structure members together without holes

-fpack-struct=<number>      Set initial maximum structure member alignment

 

写道
-fpack-struct[=n]
Without a value specified, pack all structure members together without holes. When a value is specified (which must be a small power of two), pack structure members according to this value, representing the maximum alignment (that is, objects with default alignment requirements larger than this are output potentially unaligned at the next fitting location.

Warning: the -fpack-struct switch causes GCC to generate code that is not binary compatible with code generated without that switch. Additionally, it makes the code suboptimal. Use it to conform to a non-default application binary interface.

 

也可以在程序中通过#pragma pack(n)指定。

 

#pragma pack(n) simply sets the new alignment.

 

#pragma pack() sets the alignment to the one that was in effect when compilation started (see also command-line option -fpack-struct[=n] see Code Gen Options).

 

#pragma pack(push[,n]) pushes the current alignment setting on an internal stack and then optionally sets the new alignment.

 

#pragma pack(pop) restores the alignment setting to the one saved at the top of the internal stack (and removes that stack entry). Note that #pragma pack([n]) does not influence this internal stack; thus it is possible to have #pragma pack(push) followed by multiple #pragma pack(n) instances and finalized by a single #pragma pack(pop).

 

通过-fpack-struct[=N]指定内存对齐

 

举个例子:

#include <stdio.h>

#include <stdlib.h>

#include <stddef.h>

 

struct test_s {

    int i;

    char c;

    double d;

    char a[];

};

 

#

#

#

#

 

int main(int argc, char **argv)

{

    printf("sizeof(struct test_s)=%d\n", sizeof(struct test_s));

 

    printf("offsetof(struct test_s, i)=%d, offsetof(struct test_s, c)=%d, offsetof(struct test_s, d)=%d, offsetof(struct test_s, a)=%d\n",

        offsetof(struct test_s, i),

        offsetof(struct test_s, c),

        offsetof(struct test_s, d),

        offsetof(struct test_s, a));

    return 0;

}

 

 

 

gcc -fpack-struct=4 -c offsetof_test.c  -o offsetof_test.o

gcc offsetof_test.o  -o offsetof_test

 

./offsetof_test 

sizeof(struct test_s)=16

offsetof(struct test_s, i)=0, offsetof(struct test_s, c)=4, offsetof(struct test_s, d)=8, offsetof(struct test_s, a)=16

 

 

 

 

 

gcc -fpack-struct=2 -c offsetof_test.c  -o offsetof_test.o

gcc offsetof_test.o  -o offsetof_test

 

./offsetof_test 

sizeof(struct test_s)=14

offsetof(struct test_s, i)=0, offsetof(struct test_s, c)=4, offsetof(struct test_s, d)=6, offsetof(struct test_s, a)=14

 

 

 

 

 

 

gcc -fpack-struct -c offsetof_test.c  -o offsetof_test.o

gcc offsetof_test.o  -o offsetof_test

 

./offsetof_test 

sizeof(struct test_s)=13

offsetof(struct test_s, i)=0, offsetof(struct test_s, c)=4, offsetof(struct test_s, d)=5, offsetof(struct test_s, a)=13

 

 

 

0
0
分享到:
评论

相关推荐

    C语言第12章位运算解读.pdf

    - **内存对齐**:通过位运算来调整指针地址,使其符合内存对齐规则。 - **编码和解码**:在编译器、网络协议或文件格式中,位运算用于编码和解码数据。 3. 位运算实例: - 假设变量a = 0b1010,b = 0b1100: - ...

    c++内存管理测试题及答案一套

    直接将 `char` 数组转换为 `myObj*` 可能导致内存对齐错误,因为不同类型的变量可能有不同的内存对齐要求。例如,如果 `myObj` 需要16字节对齐而 `char` 数组未按照16字节边界对齐,则可能会导致访问错误或异常行为...

    c语言设计超长整数计算器

    总的来说,设计一个C语言的超长整数计算器是一项挑战性的任务,它涉及到数据结构、算法、内存管理和软件工程实践。通过这个项目,不仅可以提升C语言编程技能,还能深入理解计算机内部的数值计算机制。在实际的final...

    《你必须知道的495个C语言问题》

    书中列出了C用户经常问的400多个经典问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等各个方面的主题,并分别给出了解答,而且结合代码示例阐明要点。 《你必须知道的495个C语言问题》结构...

    你必须知道的495个C语言问题

    第1章 声明和初始化 基本类型 1.1 我该如何决定使用哪种整数类型? 1.2 为什么不精确定义标准类型的大小?...第7章 内存分配 基本的内存分配问题 7.1 为什么这段代码不行?char*answer;printf("Typesomething...

    北语20秋《计算机应用基础》练习2.docx

    A:幻灯片页面的对齐方式 B:幻灯片的页脚 C:幻灯片的页眉 D:幻灯片编号的起始值 答案:D 最早设计计算机的目的是进行科学计算,但主要是用于___。 A:科研 B:军事 C:商业 D:管理 答案:B 在Excel中,假定一个单元格所...

    计算机组成与体系结构期末复习题

    11. **结构体变量内存布局**:在C语言中,结构体的成员按照声明的顺序存储,且可能有对齐规则。根据题目描述,结构体record的int a占据4个字节,char b占据1个字节,short c占据2个字节。因此,record.c的地址是a的...

    PE资源修复

    FileAlign --- 资源节的文件对齐方式,值只能是 0x200 或者 0x1000。 ErrBuff --- 指向一块至少具有 80 个字节空间的 Buffer 指针,在执行该函数返回错误时,接收 错误消息。 该函数适用任何 Win32 平台的编程语言...

    程序员面试宝典题目总结

    在C++中,如果需要调用由C编译器编译的函数,需要使用 `extern "C"` 来告诉编译器按照C语言的方式来链接这个函数,避免C++的名字修饰机制导致的命名冲突。 --- #### 9. 头文件中的预处理指令作用 **知识点解析:*...

    淘宝校园招聘笔试题

    - **背景**: 在C语言中,数组名可以被视为指向数组第一个元素的指针。 - **问题**: 已知一个二维数组`a[3][2]`和一个指针数组`p[3]`,若`p[0]=a[1]`,那么`*(p[0]+1)`代表的值是什么? - **答案**: 正确答案为 **D、...

    2017东大计科微机接口作业题.docx

    这篇文档主要涵盖的是计算机科学与技术领域中关于微机接口的一些基本知识,主要涉及了数据表示、微处理器、总线、内存地址计算以及系统结构等多个方面。以下是这些知识点的详细解释: 1. **负数表示**:在计算机...

    冲刺BAT练习题

    ### 第二讲:C/C++基础(上) 1. **智能指针原理及实现** - 智能指针是封装原始指针的一种对象,通过析构函数自动释放内存。 - 实现方式包括引用计数(如`std::shared_ptr`)和所有权转移(如`std::unique_ptr`)...

    C编程 面试题 F卷

    动态内存开辟在**C:堆**中。 10. **双向链表删除结点:?** - 删除 `p` 结点的程序段如下: ```c p-&gt;prev-&gt;next = p-&gt;next; p-&gt;next-&gt;prev = p-&gt;prev; free(p); ``` 11. **`int *pAddress = 0x10001000; ...

    uboott移植实验手册及技术文档

    u-boot运行到第2阶段会进入start_armboot()函数。其中nand_init()函数是对nand flash的最 初初始化函数。nand_init()函数在两个文件中实现。其调用与 CFG_NAND_LEGACY 宏有 关,如果没有定义这个宏,系统调用 ...

    2021-2022计算机二级等级考试试题及答案No.18708.docx

    14. 数据库操作:在数据库系统中,查找第2个满足条件的记录,需要先定位到第一条符合条件的记录,然后继续查找,选项C正确。 15. Word功能:Word的"自动更正"功能仅替换文字,不涉及图像。 16. 数据的定义:数据是...

    虹软c++笔试题B组

    - 定义罗马数字的基本单位:I(1)、V(5)、X(10)、L(50)、C(100)、D(500)、M(1000)。 - 分别处理各个位上的数值,例如个位、十位、百位。 - 根据数值大小组合不同的罗马数字表示,如4表示为IV,9表示...

    2021-2022计算机二级等级考试试题及答案No.17944.docx

    例如,如果设置`Interval = 1000`,则每隔1秒(1000毫秒)触发一次Timer事件。 - **应用场景**:定时器常用于游戏开发、计时器、动画控制、数据更新提醒等多种场合。 - **注意事项**:在程序中使用定时器时,需要...

Global site tag (gtag.js) - Google Analytics