位域是指信息在保存时,并不需要占用一个完整的字节,而只需要占几个或一个二进制位。为了节省空间,C语言提供了一种数据结构,叫“位域”或“位段”。
在《小许code:Go内存管理和分配策略》这篇分享中我们了解到Go是怎么对内存进行管理和分配的,那么用户的程序进程在linux系统中的内存布局是什么样的呢?我们先了解一下基础知识,然后再看Go的内存对齐。
🎬 鸽芷咕:个人主页 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》
#pragma pack (n)这个语句用于设置结构体的内存对齐方式,具体作用下面再说。在linux gcc下n可取的值为:1,2,4,当n大于4时按4处理。如果程序中没用显试写出这个语句,那么在linux gcc下,它会对所有结构体都采用#pragma pack (4)的内存对齐方式。需要注意的是,在不同的编译平台上默认的内存对齐方式是不同的。如在VC中,默认是以#pragma pack (8)的方式进行对齐。
被Go语言称为寄存器宽度的这个值,就可以理解为机器字长,也是平台对应的最大对齐边界,而数据类型的对齐边界是取类型大小与平台最大对齐边界中的较小的那个
所以上面的memory类的内存对齐是按照4字节进行的,计算机按照顺序分配内存,4字节剩余空间能放下某个类型的,就放进去,放不进去的,新往下找一块4字节的空间放 int
提示:如果把函数写到main之前,那么就不需要声明。而且一般我们都是把main写在文件最下面。
不是所有硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些特定地址处取某些特定的数据,否则就会抛出硬件异常。也就是说计算机在读取内存数据时,只能在规定的地址处读数据,而不是在内存中任意位置都会可以读取的。
我们可以看到,两个结构体s1和s2内部的数据都是两个char类型和一个int类型数据,只是存放的顺序不同,其结构体整体的大小竟然发生了改变。这就是结构体内存对齐。
在了解内存对齐之前,先来明确几个关于操作系统的概念,更加方面我们对内存对齐的理解。
众所周知,C++的空类占用1个字节的内存空间,非空类占用的空间与类内的成员有关。 但类中成员所占内存并不是连续的,拿以下代码举例:
C语言中的数据类型可以分为两种:简单数据类型和复杂数据类型,简单数据类型就是我们经常用到的整型(int)、实型(float)、字符型(char)等,复杂数据类型中有结构体(struct)、位段(struct)、枚举(enum)和联合体(union)这几种。
在C/C++中的结构体或类,存在内存对齐问题。内存对齐是为了方便计算机进行寻址,优化寻址速度的一个措施,其代价是消耗不必要的内存空间。
谈到内存对齐,早年间玩Java的时候就能偶尔打打交道,为此Java8还提供了个语法糖@Contended来帮助我们解决高速缓存cacheline内存未对齐的伪共享问题。不过Go目前涉及到类似问题,比如内存对齐带来的原子操作的问题还是需要手动处理下,毕竟Russ Cox大佬也发话了
🔍问题引导: 我们都知道sizeo(变量类型))可以得到一个变量所占内存的大小,那么,请看下面这串代码👇🏻
首先我们需要知道的是结构体是一种数据类型,它本质上是用于将不同类型的数据组合在一起形成的一个新的数据类型。
你被四只妖物追杀至一片神秘的山脚下,现在它们快追来了,你刚想穿过一片竹林,却不小心绊到了什么,一瞬间,你到达了另一个地方,映入眼帘的是一块巨大石碑,石碑上刻着这样一段代码:
结构体字节对齐 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列, 而不是简单地顺序排列,这就是内存对齐。 计算结构变量的大小必须讨论数据对齐的问题。为了使CPU存取的速度最快(这同CPU取数操作有关),c++在处理数据时经常把结构变量中的成员的大小按照4或
类对象模型是一种编程概念,用于描述和实现面向对象编程(OOP)中的类和对象。在这个模型中,类定义了对象的结构和行为,包括数据成员(属性)和成员函数(方法)。对象是类的实例,具有类的所有属性和方法。类对象模型支持封装、继承和多态等OOP特性,使得代码更加模块化、可重用和易于维护。通过类对象模型,程序员可以创建复杂的软件系统,提高开发效率和代码质量。
如果这几个问题你理解的还不是很清楚,那么请仔细阅读一下下面的内容。围绕这几个问题一一进行展开。
核心: 1.每个元素的首地址偏移量必须能整除该元素的长度。 2. 整个结构体的长度必须能整除最长元素的字节数。
有些同学可能不知道,struct 中的字段顺序不同,内存占用也有可能会相差很大。比如:
在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列,而不是简单地顺序排列,这就是内存对齐。 内存对齐的原因: 1)某些平台只能在特定的地址处访问特定类型的数据; 2)提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对
虽然所有的变量最后都会保存到特定的地址内存中去,但是相应的内存空间必须满足内存对齐的要求,主要基于存在以下两个原因:
在C++中,volatile是一个关键字,用于修饰变量,告诉编译器该变量的值可能在程序流程之外被意外修改,因此编译器不应该对该变量进行优化(如缓存变量值或重排指令顺序)。
本小节,我们学习结构的内存对齐,理解其对齐规则,内存对齐包含结构体的计算,使用宏offsetof计算偏移量,为什么要存在内存对齐?最后了解结构体的传参文章干货满满!学习起来吧😃!
在编程中,结构体是一种自定义的数据类型,它允许开发人员将不同类型的数据组合在一起,并为其定义相关属性和行为。结构体提供了一种灵活的方式来表示复杂的数据结构,使得程序设计更加模块化和可读性更高。
例如Python万物皆对象,并且将内存操作封装的很好,所以python的基本数据类型所用的内存会要远大于存放纯数据类型所占的内存,例如,我们都知道存储int型数据需要四个字节,但是使用Python 申请一个对象来存放数据的话,所用空间要远大于四个字节。
结构体的关键字是struct 后面的Stu是结构体类型名,由我们自己定义,s1,s2是结构体变量,age和name是成员,即结构体变量中有各自的成员。
在写代码的过程偶尔会用到一些宏,这些宏多定义在头文件中,通过查看头文件,就可以获取相关信息
内存对齐是计算机编程中的一个重要概念,它确保了高效的内存访问,并有可能在各种性能关键型系统和应用中产生可观的性能提升。
位模式:内存由字节组成.每个字节由8位bit组成,每个bit状态只能是0或1.所谓位模式,就是变量所占用内存的所有bit的状态的序列指针大小:一个指针的大小是多少呢?在32位操作系统上,指针大小是4个字节,在64位操作系统上,指针的大小是8字节,所以uintptr能够容纳任何指针的位模式,总的说uintptr表示的指针地址的值,可以用来进行数值计算GC不会把uintptr当作指针,uintptr不会持有一个对象,uintptr类型的目标会被GC回收
大家好啊,不知道看了网管在上周的文章Go指针的使用限制和unsafe.Pointer突破之路,你们有没有感觉Golang 比之前想的还好用呢?确实能直接读写内存,很多以前觉得无能为力的事情就不再是问题了,比如那些没对外开放只能在开源包内部用的结构体字段这下我们都有办法抓到了,起码能给程序调试带来不小的速度提升。
3)结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
这个结构体中有三部分,每个部分都称为成员或字段。这三部分中,一部分存储书名,一部分存储作者名,一部分存储价格。另外,我们必须掌握三个技巧:
我们知道当内存的边界正好对齐在相应机器字长边界上时,CPU的执行效率最高,为了保证效率,在VC++平台上内存对齐都是默认打开的,在32位机器上内存对齐的边界为4字节;比如看如下的代码:
之前遇到过这样一个情况(发现问题的结构体并不长这样, 不过为了引出问题, 改了一下):
内存对齐:#pragma pack(字节数) 如果用1,那么内存之间就没有空隙了
我们已经知道结构体是什么了,那如果我们想用结构体来描述一个学生该怎么做呢? 首先我们要进行结构体的声明。 如果我们想要描述一个学生,那我们就先来声明一个学生类型,怎么声明呢?
PS:传值调用,形参是实参的临时拷贝,开辟还要占据空间。而传址调用仅仅传递地址,节省空间
unsafe,顾名思义,是不安全的,Go定义这个包名也是这个意思,让我们尽可能的不要使用它,如果你使用它,看到了这个名字,也会想到尽可能的不要使用它,或者更小心的使用它。
一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.
在C语言中,默认的对齐数通常是编译器相关的,一般情况下默认对齐数是当前平台最宽基本类型的大小(例如在32位系统上是4字节,在64位系统上是8字节)。
得出一个结论:根据顺序不同也会造成所占内存大小不同,可是为什么会这样呢?我们继续。
Java 作为一个面向对象语言,给我们带来了多态,继承,封装等特性,使得我们可以利用这些特性很轻松的就能构建出易于扩展,易于维护的代码。作为一个Javaer,天天搞“对象”,那你写的对象究竟占用了多少内存呢?我们来看看你的“对象”是如何“败家”的。
2.注意:即使成员变量相同的两个匿名结构体,也会被编译器认为成两个结构体类型。所以不能用一个结构体类型的指针去指向另一个结构体类型的变量。
本篇文章为自定义类型系列讲解的第一篇,而本篇文章讲解的时自定义类型的第一部分内容——结构体。同时,本篇文章也是结构体内容的详解,希望对你的结构体学习有所帮助。
领取专属 10元无门槛券
手把手带您无忧上云