这个结构体对齐输出有意思

这个题目是我在群里看到大家讨论的,既然是讨论的了,那我就拿出来说说,因为笔试面试的时候,可能就会遇到这样的题目。

实例代码

#include "stdio.h"
#include "stdint.h"struct Obj {char a; //1uint32_t b;//4uint8_t c;//1uint64_t d[0];//8
};int main()
{struct Obj Op;printf("%d %d\n",sizeof(Op),sizeof(Op.d));return (0);
} 

程序输出

16 0--------------------------------
Process exited after 0.03048 seconds with return value 0
请按任意键继续. . .

这里比较令我们疑惑的是,d 的大小明明是 0,为甚结构体的大小会是 16呢?

调戏一下

看看下面代码的输出

#include "stdio.h"
#include "stdint.h"#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member)  ((char *)&((struct *)0)->member - (char *)0)#pragma pack(1)struct Obj {char a; uint32_t b;uint8_t c;uint64_t d[0];
};int main()
{struct Obj Op;PRINT_D(OFFSET(struct Obj,a)); PRINT_D(OFFSET(struct Obj,b)); PRINT_D(OFFSET(struct Obj,c)); PRINT_D(OFFSET(struct Obj,d)); printf("%d %d\n",sizeof(Op),sizeof(Op.d));return (0);
} 

程序输出

OFFSET(struct Obj,a) is 0
OFFSET(struct Obj,b) is 1
OFFSET(struct Obj,c) is 5
OFFSET(struct Obj,d) is 6
6 0--------------------------------
Process exited after 0.0108 seconds with return value 0
请按任意键继续. . .

这里的输出刚好是我们的结构体里内容的大小

解释下这段调试代码的作用

#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member)  ((char *)&((struct *)0)->member - (char *)0)

前面哪个比较简单了,就是使用 「#」这个符号把字符串带过来打印。下面的OFFSET 比较有意思,先是把 0 这个地址强制转换成我们需要的strunct ,然后呢,再读取地址减去0地址,这样就可以得出它的偏移大小了。

调戏一下2

我们改一下代码

#include "stdio.h"
#include "stdint.h"#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member)  ((char *)&((struct *)0)->member - (char *)0)#pragma pack(4)struct Obj {char a; uint32_t b;uint8_t c;uint64_t d[0];
};int main()
{struct Obj Op;PRINT_D(OFFSET(struct Obj,a)); PRINT_D(OFFSET(struct Obj,b)); PRINT_D(OFFSET(struct Obj,c)); PRINT_D(OFFSET(struct Obj,d)); printf("%d %d\n",sizeof(Op),sizeof(Op.d));return (0);
} 

程序输出

OFFSET(struct Obj,a) is 0
OFFSET(struct Obj,b) is 4
OFFSET(struct Obj,c) is 8
OFFSET(struct Obj,d) is 12
12 0--------------------------------
Process exited after 0.01165 seconds with return value 0
请按任意键继续. . .

调戏代码3

#include "stdio.h"
#include "stdint.h"#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member)  ((char *)&((struct *)0)->member - (char *)0)#pragma pack(8)struct Obj {char a; uint32_t b;uint8_t c;uint64_t d[0];
};int main()
{struct Obj Op;PRINT_D(OFFSET(struct Obj,a)); PRINT_D(OFFSET(struct Obj,b)); PRINT_D(OFFSET(struct Obj,c)); PRINT_D(OFFSET(struct Obj,d)); printf("%d %d\n",sizeof(Op),sizeof(Op.d));return (0);
} 

程序输出

OFFSET(struct Obj,a) is 0
OFFSET(struct Obj,b) is 4
OFFSET(struct Obj,c) is 8
OFFSET(struct Obj,d) is 16
16 0--------------------------------
Process exited after 0.01219 seconds with return value 0
请按任意键继续. . .

结构体对齐大小的方式,这个背下,不背下就存下

原则A:struct或者union的成员,第一个成员在偏移0的位置,之后的每个成员的起始位置必须是当前成员大小的整数倍;

原则B:如果结构体A含有结构体成员B,那么B的起始位置必须是B中最大元素大小整数倍地址;

原则C:结构体的总大小,必须是内部最大成员的整数倍;

这几个原则是在 没有 #pragma pack 的时候才起作用的,有了 #pragma pack,就按照 #pragma pack 的方式去对齐。

分析一下

基于上面的实验和理论,我们可以知道,这个笔试题的输出结果是因为 uint64_t d[] 这个搞鬼了,就是因为这个搞鬼了,我们结构体的最终大小才是 16。因为 uint64_t d 是 8个字节,这样结构体就是以 8 字节的方式对齐了。

虽然d 的不占用内存的,但是这个家伙的存在让结构体的对齐方式产生了改变,就是这么神奇。

为了验证我们的想法,我们改下程序

实例代码

#include "stdio.h"
#include "stdint.h"#define PRINT_D(intValue)     printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member)  ((char *)&((struct *)0)->member - (char *)0)struct Obj {char a; uint32_t b;uint8_t c;uint32_t d[0];
};int main()
{struct Obj Op;PRINT_D(OFFSET(struct Obj,a)); PRINT_D(OFFSET(struct Obj,b)); PRINT_D(OFFSET(struct Obj,c)); PRINT_D(OFFSET(struct Obj,d)); printf("%d %d\n",sizeof(Op),sizeof(Op.d));return (0);
} 

程序输出

OFFSET(struct Obj,a) is 0
OFFSET(struct Obj,b) is 4
OFFSET(struct Obj,c) is 8
OFFSET(struct Obj,d) is 12
12 0--------------------------------
Process exited after 0.01002 seconds with return value 0
请按任意键继续. . .

看到没?

看到没?

看到没?

这样之后,程序的输出就是 12 了,也就是说,现在的程序是按照 4字节对齐方式来对齐了。

  推荐阅读:

  专辑|Linux文章汇总

  专辑|程序人生

  专辑|C语言

嵌入式Linux

微信扫描二维码,关注我的公众号 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467454.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

声压级 matlab,语音信号处理教程(二)声音的声压级和响度

本节内容我们来看下如何用Matlab和Python计算声音的声压级和响度。声压级1. 声压级定义首先来看声压级,这个就是指的我们平时所说的声音有多少分贝。声压定义为声波在某一点产生的逾量瞬时压强的均方根值。由于声压容易被人耳感知,也易于测量&#xff0c…

javascript 之作用域-06

作用域 作用域:是指变量可访问的范围,他规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。 作用域有两种工作模式: 静态作用域 :又称为词法作用域,在编译阶段就可以决定变量的引用,…

被裁之后才明白:有一种抗风险能力,叫做会讲故事

如果你注意观察,会发现周围总有这么一种人:他说的每句话,单拎出来都没毛病,但一说出口就让人无法接受,很难说服你。尤其在职场里,这种无效沟通特别多,以至于产生了庞大的“沟通成本”&#xff1…

saltstack php,Saltstack快速入门简单汇总

[rootmaster~]# salt \* test.pingminion-1:Trueminion-2:Trueminion-3:Trueminion-4:TrueTrue代表正常,没有响应当然代表客户端没有启动或者没有认证成功之类的。指定目标主要有五种方式一: Global,即salt默认的匹配方式,能识别终…

课下测试03!03!03!题目截图及解析(不完全正确)第四周--信息安全系统设计基础...

课下测试03,也就是第三章内容,以下分析和解析仅供参考哦~ 注意!最好是对着题目看一下书,自己思考一下题目(毕竟我页数都给你标出来了),不是说这样你就能提高了,而是我正确率真不高&a…

哦!数组还能这么用,学到了!

来源:公众号【编程珠玑】作者:守望先生ID:shouwangxiansheng这个问题源于读者在阅读redis源码时的一个疑问。先看下面的代码,对于包含动态字符串成员的两个结构体Test0和Test1占用空间分别是多少呢?//来源:…

推荐开源代码2004/12/17

严正声明:本博客中的任何随笔、文章、图片等内容都不能私自转载,必须书面征得作者同意才能转载,并不能随意篡改,如要作出任何改动,必须书面征得作者同意方可,作者拥有一切权利并保留一切追究权利&#xff0…

广东,就是这么横?

昨晚的稿 今天发一下 应该有好久好久没有写篮球相关的文章了,因为之前写了被骂了,不过,今天不一样,毕竟方超巨打得这么好,不吹一下,总感觉今天不完整,骂就骂了吧,反正也不差这一次了…

在ASP.NET中利JavaScript实现控件的聚焦

在Windows应用程序中很容易控制控件的聚焦&#xff0c;但是在ASP.NET中并没有提供这样的功能&#xff0c;但是我们同样可以实现这样的功能&#xff0c;这篇文章就讲述了通过JaveScript实现在页面上某一特定控件获得焦点的功能。 下面是用到的JavaScript代码。 <script langu…

电厂各类设备原理动图,绝对让你看花眼!

▲ 火力发电流程原理▲ 核能发电流程原理▲ 水力发电流程原理▲ 光热发电原理▲ 垃圾发电原理▲ 蒸汽吸收式制冷原理▲ 尿素热解脱硝流程原理▲ 湿法脱硫工艺原理▲ 钢球磨煤机内煤的破碎原理▲ 碎煤机工作原理▲ 螺旋输送机&#xff08;绞龙&#xff09;原理▲ 多管电除尘器▲…

也谈MMU管理机制

1&#xff0c;结构&#xff1a; MMU存储器系统的结构允许对存储器系统的精细控制。大部分的控制细节由存在存储器中的转换表提供。这些表的入口定义了从1KB 到1MB 的各种存储器区域的属性。这些属性包括&#xff1a; 虚拟地址到物理地址映射 ARM 处理器产生的地址叫虚拟…

__ATTRIBUTE__ 你知多少?

_ATTRIBUTE__ 你知多少&#xff1f; 1 #include "stdio.h"2 3 /* 地址参考基准 */4 5 char r1;6 short r2;int refer;7 8 struct p9 { 10 int a; 11 12 char b; 13 14 short c; 15 16 }__attribute__((aligned(4))) pp; 17 /* 4字节对齐&#xff0c;a…

跟几位大佬共进晚餐

这是一篇几个程序员大佬聚会的聚后感文章这次聚会比较唐突&#xff0c;连总从广州专门开车来深圳看望我们&#xff0c;我们约在了某个地铁站的八合里牛肉火锅店&#xff0c;这是一个周五的下午&#xff0c;理论上是非常简单的一个周五&#xff0c;但是因为这些男人女人的存在&a…

vue.js框架搭建

安装脚手架 前提条件&#xff1a;已安装node&#xff08;4.0版本以上&#xff09;&#xff0c;npm a、全局安装 vue-cli npm install -g vue-cli 安装成功后可以通过命令行查看版本号&#xff0c;如图 b、初始化项目 新建一个文件夹命名为01vue&#xff0c;准备在此文件夹下存放…

oracle数据库imp导入,imp 导入 没有数据库

IMP-00009: 导出文件异常结束今天准备从生产库向测试库进行数据导入&#xff0c;结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误&#xff0c;google一下&#xff0c;发现可能有如下原因导致imp的数据太大&#xff0c;没有写buffer和commit两个数据库字符集不同从…

MIK C语言面试两题

这是一个读者朋友在知识星球上提到的两个笔试题&#xff0c;第一个题目比较简单&#xff0c;关键在第二个题目「编程题」&#xff0c;我文章中写的解题思路应该不是最好的&#xff0c;希望大神读者们给出更好的答案&#xff0c;让这个充满乐趣的程序世界再增添一些乐趣吧&#…

看看大疆的C语言面试题

惯例&#xff0c;这笔试题也是一个读者朋友发给我的&#xff0c;简单看了下&#xff0c;并不觉得这是一个非常困难的题目&#xff0c;最近是校招准备的时候&#xff0c;很多人给我说发面试题对大家有帮助。这个题目面试官强调了这个跑在64位系统下。代码如下:#define mal(x,y) …

RocketMQ实战(一)

阿里巴巴有2大核心的分布式技术&#xff0c;一个是OceanBase&#xff0c;另一个就是RocketMQ。在实际项目中已经领教过RocketMQ的强大&#xff0c;本人计划写一个RocketMQ实战系列&#xff0c;将涵盖RocketMQ的简介&#xff0c;环境搭建&#xff0c;初步使用、API详解、架构分析…

C面试总结文档

最近很多人有参加面试&#xff0c;面试就避免不了笔试&#xff0c;嵌入式面试的话&#xff0c;避免不了C语言&#xff0c;所以给大家准备了两份pdf C语言面试总结的文档。在本公众号回复 「C面试」获取pdf下载链接推荐阅读&#xff1a;专辑|Linux文章汇总专辑|程序人生专辑|C语…

程序员到底怎么了

我们是这样的一群人&#xff1a;每天都在“努力”的工作着&#xff0c;每天都和计算机打交道&#xff0c;泡在网上&#xff0c;打游戏&#xff0c;查资料&#xff0c;发微博。可是有一天&#xff0c;突然意识到&#xff0c;我们的未来在哪里&#xff0c;每个月那点可怜的工资&a…