关于struct的内存地址分配问题

struct 结构体的定义及初始化问题 /*****************************/ //以上说明,结构体也没啥特殊的 ,跟普通的数据类型定义变量是一样的。。。 //程序在定义变量的时候,已经分配好了内存空间,只是里面存储的数据是随机的,是垃圾值。 //对于非指针变量来说,,照样可以输出或者操作,仅仅是可能会得到不正确的结果,程序是可以正常运行的。 //对于指针变量来说,同样。但是其内存空间存储的值 是被当做另外一个地址看待的,,所以 不初始化成有效的地址值得话, //程序可能会崩溃或者遇到不可预料的结果。 //注:memset 阅读详情

一直对内存地址的分配有些疑惑,故做实验研究一下:

先定义一个Struct,里面包含一个enum,一个short,一个char*存放字符串,一个short*存放short数组。

typedef struct MyStruct
{
	enum MyEnum {
		enum1, enum2, enum3, enum4
	};
	short short1;
	char * char1;
	short * arrshort;
}*PtrMyStruct;

之后定义一个类,里面的private成员变量定义该struct,并加入两个简单的方法永远设置和相关显示:

class myClass
{
private:
	MyStruct mystruct;
public:
	myClass();
        ~myClass();
	void setMyStruct();
	void showStructInfo();
};

实现具体函数

void myClass::setMyStruct()
{
	PtrMyStruct ptrmystruct = &mystruct;
	ptrmystruct->char1 = "hello,this is test";
	ptrmystruct->short1 = 125;
	ptrmystruct->arrshort = new short[4]{4, 32, 21, 57};

};

void myClass::showStructInfo()
{
	PtrMyStruct ptrmystruct = &mystruct;
	cout << "sizeof(mystruct):"<<sizeof(mystruct) << endl;
	cout << "sizeof(ptrmystruct):" << sizeof(ptrmystruct) << endl;
	cout << "sizeof(mystruct.enum1):" << sizeof(mystruct.enum1) << endl;
	cout << "sizeof(short *):" << sizeof(short *) << endl;
	cout << "sizeof(short):" << sizeof(short) << endl;
	cout << "sizeof(char *):" << sizeof(char *) << endl;
	cout << "sizeof(char):" << sizeof(char) << endl;
	cout << "&mystruct:" << &mystruct << endl;
	cout << "&ptrmystruct->short1:" << &ptrmystruct->short1 << endl;
	cout << "&mystruct.char1:" << &mystruct.char1 << endl;
	cout << "&mystruct.arrshort:" << &mystruct.arrshort << endl;
	cout << "ptrmystruct->short1:" << ptrmystruct->short1 << endl;
	cout << "ptrmystruct->char1:" << ptrmystruct->char1 << endl;
	cout << "ptrmystruct->arrshort:" << ptrmystruct->arrshort << endl;
	cout << "ptrmystruct->arrshort[0]:" << ptrmystruct->arrshort[0] << endl;
	cout << "ptrmystruct->arrshort[1]:" << ptrmystruct->arrshort[1] << endl;
	cout << "ptrmystruct->arrshort[2]:" << ptrmystruct->arrshort[2] << endl;
	cout << "ptrmystruct->arrshort[3]:" << ptrmystruct->arrshort[3] << endl;
}

在32位编译环境下Debug的结果:

因为是32位,即8字节,因此地址符的寻址路径有8位。

可以看到mystruct占用了12byte,起始地址是004FF6F8,struct中的第一个变量是short1,short类型占用2byte,因此地址是004FF6F8->004FF6F9;第二个变量地址char1,占用了4byte,地址是004FF6FC->004FF6FF,第三个变量arrshort,占用4byte,地址是004FF700->004FF703。那么有两个问题就来了:

一是arrshort数组的首地址是004FF700,直接调用ptrmystruct->arrshort不加取地址符得到的地址是什么?是new short[4]数组首元素的地址吗?

二是004FF6FA、004FF6FB这两个地址被分配给了谁?是嵌套定义在struct里面的enum?但是enum中的数据类型实际就是int,占4byte,为何对不上号?

那么我们来尝试看下arrshort[0]和MyEnum的地址:

第一个疑问与设想的一样,short数组中的每个short占用new出来的2byte,arrshort这个short指针变量的值是该数组首元素的地址,而arrshort的地址在mystruct开辟的12byte空间中的最后4byte。

然而&mystruct.enum1的操作是不合法的,答案显而易见,mystruct.enum1是数值常量=0,没有内存地址,那么常量存在哪里的?事实是数值常量是一个临时变量,它存放在一个随机的临时栈空间地址中,生命周期未知,所以对它取地址没有意义。

那么short1后面的2byte空间的占用究竟是怎么回事?不妨直接手动输入该地址并通过*取得它的值来看看?插入代码:

cout << "after short1:" << *(&ptrmystruct->short1 + 0x00000002) << endl;
cout << "typeid(after short1):" << typeid(*(&ptrmystruct->short1 + 0x00000002)).name() << endl;

看一下输出结果:

经多次实验发现该地址被一个固定short值-25808所占,且不受short1取值的影响。所以这个值到底是哪里来的?是系统占位符?

查询资料果然如此,原来编译器为了struct中数据的存储速度,可能会用一种数据对齐的机制增加struct的大小,会根据struct中占用字节数最大的数据类型为标准,不足这个字节数的其他数据类型会自动在之后增加字节。如例子中的最大字节数是4byte,那么short类型的short1之后就会被系统自动填充2byte,至于这个自动填充的值从何而来就不得而知了。

 

彩蛋...不妨比较一下32位和64位的运行结果:

可以看到基本数据类型char、short、int的字节数在32位和64位的编译环境下是不变的,而指针和结构体,包括指向结构体的指针在64位下的字节数都翻倍了。

首先分析内存地址的位数,32位编译下,寻址空间是2^32byte,我们用16进制表示地址的话可利用的就是16^8byte,所以一个地址占用8byte。64位下,寻址空间是2^64byte,16进制表示是16^16,所以一个地址占用16byte。

其次指针的地址大小,原理不简单,但两句话可以解决问题(参考自:指针变量占用内存的大小):

1.指针所占用的空间与指针指向的内容和内容的大小无关。

2.32位编译时,指针占4个字节;64位编译时时,指针占8个字节。

正是由于结构体中有指针,所以结构体的所有数据都会被扩展为8byte,所以sizeof(struct)=3*8=24

Union和Struct的内存分配 http://www.2cto.com/kf/201106/92935.html 嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。也就是说Big-endian模式符合人的习惯,而Little-endian更加方 阅读详情

相关推荐

用结构体的方式来操作单片机内eeprom进行数据掉电保存.pdf

用结构体的方式来操作单片机内eeprom进行数据掉电保存.pdf

写宏定义:得到一个field在结构体(struct type)中的偏移量。

#define OFFSETOF(type, field) ((size_t)&(((type *)0)->field))(type *)0:把0地址当成type类型的指针。((type *)0)->field:对应域的变量。&((type *)0)->field:取该变量的地址

myworldbig的专栏 1608

Python画常见分布的概率密度图

因为做概随大作业,了解了一些python画概率分布图的方法。

qq_44807946的博客 5204

STM32-C语言结构体地址

是unsigned long类型的变量,在windows的64位机器中占4个字节,所以person->age的地址 = 4 + person->member_num的地址 = 0x1f1434。是unsigned short类型的变量,可以等同于short占用的字节,都是2,所以person->addr的地址 = 2 + person->age的地址 = 0x1f1436。的地址,与它指向的结构体的第一个成员(person->pprenode)地址是一样的,都是0x1f1420。

ltqdxl的博客 2069

DenseNet训练实践

在Cifar10上训练DenseNet,复现DenseNet论文的结果是后面两篇分析工作的基础,因此本系列的第一篇,将首先讨论深度学习的训练过程中的几个方面: 1. 数据集 1.1 Cifar10数据集 Cifar10数据集有6万张32X32的图片,是一个小型的数据集,但数据的复杂度足够,下图来自DenseNet论文,可以看出,不用数据增强的话,ResNet的正确率也很难超过90%,因而足以用来验...

一蓑烟雨任平生 1万+

用R搞定生态溯源:MixSIAR贝叶斯混合模型从安装到出图的保姆级教程

本文提供了一份详细的MixSIAR贝叶斯混合模型教程,涵盖从R环境配置、数据预处理到模型参数设置和可视化分析的完整流程。通过实战案例和常见问题解决方案,帮助生态学研究者快速掌握混合模型在生态溯源中的应用,提升数据分析效率。

weixin_42531516的博客 466

单片机关于typedef定义结构体,存储占用问题

在开发单片机中,通常用到与网络通信,在接收通信协议的时候,避免不了使用typedef定义结构体。这里测试如果用typedef定义结构体时不占用单片机内存(ZI-data), 1、不写结构体定义时:生成的代码空间为: 2、在.h文件中增加一个结构体,如下: 这时生成的代码空间为: 这时,可以发现用typedef定义的结构体时,单片机存储空间没有变化 3、该结构体定义全局变量时,...

likai_lian的专栏 2241

mpv 源码深度解析:从 MPlayer 遗产到现代媒体播放器架构

做减法优于做加法:不值得维护的功能就移除(DVD 菜单、流缓存等)单线程事件驱动:核心逻辑在单线程中运行,通过 dispatch 队列处理并发分离关注点:demuxer 线程、VO 线程、脚本线程各司其职质量优先:使用 GPU 着色器而非固定功能硬件,默认软件解码(更可靠)可嵌入性:libmpv API 让 mpv 可以作为库嵌入其他应用mpv 是一个用纯 C 实现的、工程质量极高的媒体播放器。它的代码库展示了一个事实:C 语言完全可以通过良好的架构设计来实现复杂的多媒体系统。

世界那么大,我想去看看~【随心更新,看世界去了😄】 85

C语言 编译预处理、typedef与动态储存分配

本博文为半摘记性质,收集整理自菜鸟教程、《二级C》及其他互联网资料 —— 编译预处理不属于C语言语句的范畴,相当于一个文本替换过程。 C语言允许用户自行定义类型说明符,即可以自行为原有的数据类型取别名(并没有生成新的数据类型) 动态内存分配指在程序运行中实时申请内存分配,通常用于预先不知道需要存储的文本长度的情况下

追求。 761

Codex 接入国产大模型完整指南:CC-Switch 与 Codex++ 的工程化选型

Codex 默认走 OpenAI Responses API,而 DeepSeek、GLM、Kimi 等国产模型只提供 OpenAI-compatible 的 Chat Completions——两套协议在消息结构、流式响应、reasoning 字段、tool call 上都不兼容,所以"改个 Base URL + 填国产 Key"这种朴素操作根本跑不通。本文从架构师视角出发,把 Codex 接入国产大模型这件事讲透:先剖析协议链路到底卡在哪,再对比 CC-Switch和 Codex++两套主流方案的差异

少说,多做 3844

1.2 Spring AI V1.1.2版本 架构全景:模块拆解与设计哲学

Override// 检查请求的 Token 数量throw new TokenLimitExceededException("请求超出 Token 限制");@Override// 记录实际消耗的 Token@Override// 最先执行// 接口// 不同实现// 使用方无需改变定义抽象,保证可移植性提供 Fluent API,提升开发体验Advisor 机制提供扩展点,支持灵活定制模块化设计让你只引入需要的依赖自动配置。

小林夕程的博客 219

C语言之struct大小、首地址与内存对齐

被问到如下问题: 给定一个结构体中某个变量地址,可否得到结构体变量的地址? 答案是可以,但是对不同的场合有不同的结果;这与微处理器平台、编译器的处理不可分割。 首先,对于处理器,大尾端、小尾端的因素必须考虑; 其次: 一、ANSIC标准中并没有规定,相邻声明的变量在内存中一定要相邻。 为了程序的高效性,内存对齐问题由编译器自行灵活处理,这样导致相邻的变量之间可能会有一些填充

Kayven@数据 1万+

【AI Agent教程】各种Agent开发框架都是如何实现ReAct思想的?深入源码学习一下

驱动大模型有很多种方式,例如纯Prompt方式、思维链方式、ReAct方式等。ReAct 方式是 AI Agent 最常用的实现思路之一,它强调在执行任务时结合推理(Reasoning)和行动(Acting)两个方面,使得Agent能够在复杂和动态的环境中更有效地工作。

同学小张的博客 6396

线程概念详细总结

什么是线程 LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下) 进程:独立地址空间,拥有PCB 线程:也有PCB,但没有独立的地址空间(共享) 区别:在于是否共享地址空间。 独居(进程);合租(线程)。 Linux下:线程是最小的执行单位;进程是最小分配资源单位,可看成是只有一个线程的进程。 Linux内核线程实现原理 类Unix系统中,早期是没有“线程”概念的,80年代才引入,借助进程机制实现出了线程的概念。因此在这类系统中,进程和线程关系密切。 轻量级

qq_37535749的博客 816

ROS2 Nav2 - 模型预测路径积分控制器(MPPI)

这是一个预测控制器(局部轨迹规划器),用于实现模型预测路径积分(MPPI)算法,以跟踪具有自适应避障功能的路径。它包含基于插件的 critic 函数,可影响算法的行为。它由 Aleksei Budyakov 创建,由 Steve Macenski 为 Nav2 进行改编和开发。该插件实现了 nav2_core::Controller(控制器)接口,可在整个导航堆栈中作为控制器服务器的动作服务器(controller_server)中的局部轨迹规划器使用。据测定,该控制器可在中等英特尔处理器(第四代 i5)上

机器人控制与仿真应用 3907

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

C --struct union内存分配

嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。也就是说Big-endian模式符合人的习惯,而Little-endian更加方便计算机操作。 例如,16bit宽的数0x1234在Little-endian模式CPU内

manchestermi的博客 1093

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【C++】class/struct的内存分配问题

说明:以32位系统为例,以class举例说明(C++的struct和class基本是一样的)内存分配的问题。 1、空类 class A{ }; sizeof(A) = 1 C++标准规定,不允许一个对象(当然包括类对象)的大小为0,不同的对象不能具有相同的地址。这是由于: new需要分配不同的内存地址,不能分配内存大小为0的空间 避免除以 sizeof(T)时得到除以0错误 故使...

Magnum的博客 1891

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Struct和union内存地址

typedef struct foo { int a; char b; char c; int d; long e; float f; double g; }foo_t; union MyUnion { int a; char b; char c; int d; long e; float f; double g; }MyUnion; int main() { p...

qq_37041925的博客 426

linux关于程序的user,c – “get_user_pages”如何工作(对于linux驱动程序)

在Linux PCI驱动程序上工作,现在我正在尝试使用scatter / gather为DMA编写代码.目前,我已经了解到要直接从用户空间访问DMA数据,我们需要将用户空间页面固定到内核空间.要做到这一点,我们有get_user_pages,其完整定义如下:int get_user_pages(struct task_struct * tsk,struct mm_struct * mm,unsig...

weixin_39917046的博客 209

结构体struct 内存分配规则及地址运算

参考:https://blog.youkuaiyun.com/qq_44443986/article/details/110082316。, 就是一个地址,其地址-2就是基于该地址向后移动2个单位空间,而该地址是4,其单位空间大小为4,因此。, n代表一个整数)并不是以字节为单位,而是以。类型的内存空间, 其大小为4, 取地址后,为8 (64位系统下)。解释: 在 C++ 中,

AlbertChou1的博客 832

【算法学习笔记- ListNode struct创建、遍历】

Context 我使用的struct创建的,GitHub,源码地址 Struct遇到的坑 struct ListNode { int m_nValue; ListNode* m_next; }; ListNode test1; cout << "\ntest: " << &test1; ...

zhjali123的专栏 2097

C_内存_内存地址_指针类型

C 和 C++ 是系统级编程语言,它们的设计目标之一是允许开发者直接与硬件交互并高效地管理资源。指针提供了极高的效率和灵活性,特别是在性能敏感的应用中。mallocfreenewdeleteJava 和 Python 中也有类似的功能(如对象引用和垃圾回收),但这些功能是由运行时环境自动管理的,程序员无法直接干预。指针是实现复杂数据结构(如链表、树、图等)的核心工具。在 Java 中,类似的结构可以通过对象引用来实现,但底层的内存管理和指针操作是由 JVM 自动完成的。

qq_33552379的博客 938

c语言中union嵌套struct

union { /*定义一个联合*/ char i[60]; int x; struct { /*在联合中定义一个结构*/ char first; char second; char third; char fourth; char five; }str; }number; strcpy(number.i, "abcde"); printf("%s\n",number.i)

HzjCsdn的博客 6257

C++之空类或空struct的sizeof是1

C++中空类和空结构体的sizeof结果为1,这是为了保证对象地址的唯一性。编译器会为空的类/结构体自动分配至少1字节内存,防止对象指针无法区分。静态成员不影响对象大小,而虚函数会因虚表指针增加对象体积(32位系统4字节,64位系统8字节)。这一设计确保了数组元素等场景下不同对象拥有独立地址。

hzjsnsj的博客 472

值类型 vs 引用类型性能陷阱,Struct和Class你用对了吗?

深入解析C#结构体(Struct)与类(Class)区别,帮你避开值类型与引用类型的性能陷阱。涵盖适用场景、内存分配机制及选择原则,提升程序效率。开发者必读,值得收藏。

ProceChat的博客 757
上一篇: C++ Map 通过GetNextAssoc()根据value找key
下一篇: ...... 处有未经处理的异常: 0xC0000005: 读取位置 0xcccccccc 时发生访问冲突
codingEMIPark
博客等级 码龄10年 0粉丝 5原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值