跟我一起学C++之从C到C++(结构体内存对齐)

1.什么是内存对齐

(1)      编译器为每个“数据单元”按排在某个合适的位置上。

(2)      C、C++语言非常灵活,它允许你干涉“内存对齐”。也就是可以人为的设置编译器的对齐方式。

2.为什么要对齐

性能原因:在对齐的地址上访问数据快。如果是字节对齐方式存储的话,CPU读取的时候只需要进行一个总线周期即可全部读取完毕,如果不对齐的话,对于32位的系统,CPU读取的时候一般架构都是从偶地址开启的,一次只能读取4个字节,因此需要两个总线周期才能完成,并且还需要进行寄存器里面的数据合并,还需要进行数据移位那么这样的效率就是很低的。

3.如何对齐

(1)      第一个数据成员放在offset为0的位置

(2)      其它成员对齐至min(sizeof(member),#pragma pack所指定的值)的整数倍。

(3)整个结构体也要对齐,结构体总大小对齐至各个成员中最大对齐数的整数倍。

代码示例:

//main.cpp
#include <iostream>
using namespace std;
#include <stdio.h>

#pragma pack(2)//修改对齐数
struct Test
{
	char a;//1个字节
	double b;//8个字节
	char c;//1个字节
};
#pragma pack()

//对齐规则
//1.第一个成员与结构体变量的偏移量为0。也就是&test = &test.a;
//2.其它成员要对齐到某个数字(对齐数)的整倍数的地址
//3.对齐数取编译器预设的一个对齐整数与该成员大小的较小值,VS中是默认值是8,所以上述打印出来就是24,取值可以是1,2,4,8,16
//4.结构体总大小为最大对齐数的整数倍

int main(void)
{
	Test test;
	//&test = &test.a;
	char *p= (char*)&test;
	//cout<<p<<endl;
	printf("结构体起始地址:%p\n", p);
	p = &test.a;
	printf("a起始地址:%p\n", p);
	p = (char *)&test.b;
	printf("b起始地址:%p\n", p);
	p = &test.c;
	printf("c起始地址:%p\n", p);

	cout<<sizeof(Test)<<endl;
	return 0;
}

运行结果:

以上例结构体为例,取值范围在VS中和在linux系统中的g++编译器,不同的对齐数的起止偏移地址,以及占用字节数如下所示:


编译器


VS


G++


对齐数


起始地址--结束地址


占用字节数


起始地址--结束地址


占用字节数


1


a:0--0

b:1—9

c:10-10


10


a:0--0

b:1—9

c:10-10


10


2


a:0--1

b:2—10

c:11-12


12


a:0--1

b:2—10

c:11-12


12


4


a:0--3

b:4—11

c:12-15


16


a:0--3

b:4—11

c:12-15


16


8


a:0--7

b:8—15

c:16-24


24


不支持


不支持


16


a:0--15

b:16—31

c:32-47


48


不支持


不支持

时间: 2024-10-09 18:03:56

跟我一起学C++之从C到C++(结构体内存对齐)的相关文章

[从产品角度学EXCEL 02]-EXCEL里的树形结构

这是<从产品角度学EXCEL>系列第三篇. 前言请看: 0 为什么要关注EXCEL的本质 1 excel是怎样运作的 或者你可以去微信公众号@尾巴说数 获得连载目录. 本文仅由尾巴本人发布于特定网站.不接受任何无授权转载,如需转载,请先联系我,非常感谢. 2 EXCEL里的树形结构 这段时间,上海街边的树上陆陆续续长出了嫩芽,放眼望去有各种层次的绿色,格外好看.我们今天的话题,恰好也与树有关.只不过,树都是往天空伸展枝叶的,而我们这里讨论的‘树’,却是由根部出发,逐行逐行往下延展.伸展. 还记

C编译器剖析_1.5 结合C语言来学汇编_指针、数组和结构体

让我们再来看一份C代码,及其经UCC编译器编译后产生的主要汇编代码,如图1.33所示,其中包含了数组.指针和结构体. 图1.33 数组.指针和结构体 按照C的语义,图1.33第9行的C代码是对局部数组number的初始化,需要把number[0]初始化为2015,而数组中的其他元素皆被初始化为0.UCC编译器采取的翻译方法是:先调用memset函数来把数组number所占的内存空间清0,然后再把number[0]设为2015,如图1.33的第17至24行所示.C库函数memset的API如下所示

小白学开发(iOS)OC_ 常用结构体(2015-08-14)

// //  main.m //  常用结构体 // //  Created by admin on 15/8/13. //  Copyright (c) 2015年 admin. All rights reserved. // #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { //      1. 表示范围:NSRange 结构体 NSRange ran

小白学Linux(三)--文件系统基本结构

Linux文件系统是一个倒立的单根树状结构,文件名称严格区分大小写(windows系统则是对大小写不明感的).路径用“/”分隔,跟windows的“\”不同. 这里我画了一张一般Linux系统的正常目录结构图: 单根指最顶级的目录“/”,下面每个目录的大致作用如下: bin:保存常用的可执行的二进制文件(命令).sbin文件夹下只有超极用户(root)才能执行 boot:引导目录,系统引导启动文件.包含内核文件vmlinuz开头那个文件. dev:设备目录,计算机的所有硬件设备.所有硬件每个抽象

【转】朱兆祺教你如何攻破C语言学习、笔试与机试的难点(连载)

原文网址:http://bbs.elecfans.com/jishu_354666_1_1.html 再过1个月又是一年应届毕业生应聘的高峰期了,为了方便应届毕业生应聘,笔者将大学四年C语言知识及去年本人C语言笔试难点进行梳理,希望能对今年应届毕业生的应聘有所帮助. 2013年10月18日更新-->    攻破C语言这个帖子更新到这里,我不仅仅是为了补充大学学生遗漏的知识,我更重要的是希望通过我的经验,你们实际项目中的C语言写得漂亮,写出属于你的风格.“朱兆祺STM32手记”(http://bb

C语言基础(转载自大海笔记)

# C语言基础2015年03月26日10:04:411.    语言排行榜C——java——objective-C2.    进制:进制:进位机制.用普通的话讲,应该为人为的定义一种度量来标识一样东西.计算机常用的进制有:十进制.二进制.八进制和十六进制.?    十进制:0-9(十个指头)(进制表示基数:10)?    二进制:0,1(基数为2)?    八进制:0-7(基数为8)?    十六进制:0-9,A-F(基数为16)    可以有很多进制,比如分钟为60进制等等.3.    位权为

黑马程序员— C语言基础之结构体、枚举、预处理、typedef的使用

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 本章我们主要来学习之前所学的复杂数据类型中的同样也很重要的结构体和枚举的知识,以及C语言中的预处理,typedef的使用,简单介绍一下static和extern等一些比较杂的知识.在开始本章的新知识学习之前首先我们来学习一下根据变量的作用域的不同C语言一般把变量分为两种,局部变量和全局变量.下面我们来分别比较和体会一下这两种不同的变量类型: 根据变量的作用域,可以分为: 1. 局部变量: a.

内存对齐之深度探索

编译器为什么要替我们内存对齐? 学了计算机组成原理,了解了内存的基本单元是一个字节,内存可以随机寻址,于是乎我天真的认为内存就是一个字节型的容器,基本单位是单个字节. Figure 1. 我眼中的内存空间布局 悲剧的是,内存读写的真正访问者cpu不是这么想的.cpu是根据内存访问粒度(memory access granularity,下文简写成MAG)来读取内存,MAG就是cpu一次内存访问操作的数据量,具体数值依赖于特定的平台,一般是2byte.4byte.8byte. Figure 2.

Linux输入设备详解

<什么是Linux输入设备> ?简介 Linux输入设备总类繁杂,常见的包括有按键.键盘.触摸屏.鼠标.摇杆等等,他们本身就是字符设备,而linux内核将这些设备的共同性抽象出来,简化驱动开发建立了一个input子系统.子系统共分为三层,如图1所示. 图1  input输入子系统 ?驱动层 驱动层和硬件相关,直接捕捉和获取硬件设备的数据信息等(包括触摸屏被按下.按下位置.鼠标移动.键盘按下等等),然后将数据信息报告到核心层. ?核心层 核心层负责连接驱动层和事件处理层,设备驱动(device