首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

为什么我的C结构必须添加一个奇怪的int,它会影响指针?

在C语言中,结构体(Structure)是一种用户自定义的数据类型,它可以包含多个不同类型的成员变量。当我们定义一个结构体时,可以为其添加一个奇怪的int类型成员变量,这个int类型成员变量可能会影响指针。

这个奇怪的int类型成员变量通常被称为“填充字段”(Padding Field)或者“对齐字段”(Alignment Field)。它的作用是为了满足内存对齐的要求。

内存对齐是指在存储数据时,数据的起始地址必须是某个特定值的倍数。这是因为处理器在读取内存时,通常会按照特定的字节大小进行读取,如果数据的起始地址不是特定值的倍数,就会导致处理器需要进行额外的操作来读取数据,从而降低了读取效率。

在结构体中,成员变量的存储顺序是按照定义的顺序进行的,但是编译器会根据数据类型的大小和对齐要求来进行内存布局。为了满足对齐要求,编译器可能会在结构体的成员变量之间插入一些填充字段。

这个奇怪的int类型成员变量就是为了填充而存在的,它的大小通常是根据前面的成员变量的大小和对齐要求来确定的。通过添加这个填充字段,可以确保结构体的起始地址和成员变量的起始地址都满足对齐要求,从而提高了访问结构体成员变量的效率。

这个奇怪的int类型成员变量对指针的影响是因为指针的值是一个地址,它指向的是结构体的起始地址。如果结构体的起始地址不满足对齐要求,那么指针指向的地址就不是有效的地址,这样就无法正确地访问结构体的成员变量。

总结起来,为了满足内存对齐的要求,我们在定义结构体时可能需要添加一个奇怪的int类型成员变量作为填充字段。这个填充字段的存在可以提高结构体成员变量的访问效率,同时也保证了指针指向的地址是有效的。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

一个C#开发者重温C++心路历程

前言 这是一篇C#开发重新学习C++体验文章。 作为一个C#开发为什么要重新学习C++呢?...在和很多C++开发者沟通时候,发现他们都有一个非常奇怪特点,都很爱装X,都觉得自己技术很好,还很爱瞧不起人;但如果多交流,会发现更奇怪问题,他们几乎都不懂代码设计,代码写也都很烂。...对此,只能说,好麻烦。。。首先,缺失基础类型这种事,就很奇怪,其次不是一个头文件东西,定义到一个命名空间下,也容易让人混乱。 不过,对于C++,这么做好像已经是最优解了。...还有更麻烦。 比如,想在定义结构体里使用自身类型,要怎么定义呢? 因为在C++里,变量定义必须按照先声明后使用【绝对顺序】,那么,在定义时就使用自身类型,编译器会提示错误。...kiba结构实例,定义定义一个kiba结构指针,并把kinstance地址给该指针

83630

一个printf(结构指针)引发血案

为什么写这篇文章 在上周六,在公众号里发了一篇文章:C语言指针-从底层原理到花式技巧,用图文和代码帮你讲解透彻,以直白语言、一目了然图片来解释指针底层逻辑,有一位小伙伴对文中代码进行测试,发现一个比较奇怪问题...把发来测试代码进行验证,思考好久也无法解释为什么会出现那么奇怪打印结果。 为了整理思路,到阳台抽根烟。晚上风很大,一根烟抽了一半,风抽了一半,可能风也有自己烦恼。...测试环境 3.1 操作系统 每个人电脑环境都是不一样,包括操作系统、编译器、编译器版本,也许任何一个小差别都会导致一些奇奇怪现象。...从现象上看,似乎是 printf 语句在执行过程中打印第一个数字之后,影响到了指针 p 值,但是具体是怎么影响说不清楚,而且它是系统里库函数,肯定不能改变 p 值。...那就继续用结构体变量来测试,因为上面的测试代码是结构体变量数组,现在我们把数组影响去掉,只对单独一个结构体变量进行测试: Student s = {1, "a"}; printf("%d \

88820
  • 一个printf(结构指针)引发血案

    为什么写这篇文章 在上周六,在公众号里发了一篇文章:C语言指针-从底层原理到花式技巧,用图文和代码帮你讲解透彻,以直白语言、一目了然图片来解释指针底层逻辑,有一位小伙伴对文中代码进行测试,发现一个比较奇怪问题...把发来测试代码进行验证,思考好久也无法解释为什么会出现那么奇怪打印结果。 为了整理思路,到阳台抽根烟。晚上风很大,一根烟抽了一半,风抽了一半,可能风也有自己烦恼。...测试环境 3.1 操作系统 每个人电脑环境都是不一样,包括操作系统、编译器、编译器版本,也许任何一个小差别都会导致一些奇奇怪现象。...从现象上看,似乎是 printf 语句在执行过程中打印第一个数字之后,影响到了指针 p 值,但是具体是怎么影响说不清楚,而且它是系统里库函数,肯定不能改变 p 值。...那就继续用结构体变量来测试,因为上面的测试代码是结构体变量数组,现在我们把数组影响去掉,只对单独一个结构体变量进行测试: Student s = {1, "a"}; printf("%d \

    69420

    Go并不需要Java风格GC

    内存碎片及其对GC设计影响为什么这对Java很重要,但对Go就不那么重要。 值类型以及它们如何改变GC。 分代垃圾收集器,以及Go为什么不需要它。...在Go语言中,可以做和C/C++一样事情,并定义一个像这样结构: type Sha1 struct { data [20]byte } 这些字节将位于一个完整内存块中。...这会产生安全性较低且更容易崩溃代码。 必须是在堆栈上分配纯值类型(所有结构字段也必须是值类型)。 在fixed范围内,fixed关键字关闭了垃圾收集。...如果没有值对象和真正指针,在分配大型数组或复杂数据结构时,它将总是以大量对象告终。因此,它需要分代GC。 分配更少对象需求对Go语言有利。但Go语言还有另一个技巧。...C#开发人员会尽量减少大值对象使用,因为不能安全地使用与指针相关代码。我们必须假设c#开发人员更喜欢复制值类型而不是使用指针,因为这可以在CLR中安全地完成。这自然会带来更高开销。

    91830

    程序员C语言快速上手——高级篇(九)

    A { char b; int a; short c; }; 打印结果: siez is 12 这里就发生了很奇怪现象,第一次我们使用sizeof获取结构体占用内存大小是8...,当调整成员变量声明顺序后,即将char b;和int a;顺序交换,其他都不变,结构体占用内存大小增加了,变成了12,为什么会出现这样情况呢?...编译器为了提升内存访问性能,它会做一件事,用通俗的话说,它会结构体分组访问,通常在用32位来表示int类型硬件平台上,它会将每四个字节分成一组来进行访问,这样可以提升内存访问效率。...想,正常情况下这一趟都是装不下三个人必须分两批次,而且一个人也不能被分成两半,这个吨位大的人只能占后排两个位置是比较合理,剩下两个人,一个坐副驾驶,一个再打另一辆车。...这种用法有一个用处,如果只指定声明一个结构体变量,那么全局就只有一个结构体变量,后面无法定义新结构体变量了。

    1.6K20

    赵晨雨: 从微观角度来看linux内核设计

    和交换两个数字想法一样,通过一个中转值来存放,就可以隔离影响了 ? 这里就有一些内核代码中味道了,注意一个细节,这里第四行没有括号了,为什么?这里就是因为语句表达式了,不存在上面的影响了。...这就是究极形态了,我们添加了第四行代码,来看&_min1,它意思是取_min1地址,而&_min2意思是取_min2地址,我们也知道,这两个地址肯定不可能是一样,那为什么还要这样写呢?...这里就很巧妙了,当两个变量类型不同时,对应地址,也就是指针类型也不相同,比如一个int类型,一个是char类型,那么指向他们指针就是int *和char *,这两个指针在比较时候,就比较是类型了...这里要注意一下,那就是为什么只通过TYPE和MEMBER就可以得到偏移,一开始认为是内核中这个类型结构体多了,到底用是哪一个结构体来得到,最后发现,并没有关系,因为我们需要是字节数,与实际这个字段赋什么样值并没有关系...既然是中转,那么类型就必须要求一致了,所以我们要得到和这个成员一致类型,就通过typeof来得到了,将0强制类型转换成这个这个结构指针类型,然后访问这个变量,(注意仔细看代码,这里代码和offsetof

    77520

    数据结构(一):数组篇

    解释: 其实也不知道为什么不把这个问题给办了,所以就参考前边那句话吧,读书少,不要问我。 ---- 细节决定成败 直接初始化字符数组char是特殊,这种初始化需要一个null作为结尾。...return 0; } ---- 传递数组给函数 C++ 中,可以通过指定不带索引数组名来传递一个指向数组指针。...C++ 传数组给一个函数,数组类型自动转换为指针类型,因而传实际是地址。...test.at(2); //用这个比直接用下标要好地方在于它会检测越界 头尾指针 这四个函数区别要清楚:begin()、end()、front()、back()。喜欢称它们为头尾指针。...也不知道为什么有人要就这些区别长篇大论。 begin():指向容器一个元素地址。 front():指向容器一个元素值。

    66440

    Golang中函数传参存在引用传递吗?

    继上篇文章后,继续来探讨下面的几个问题: 函数传参中值传递、指针传递与引用传递到底有什么不一样? 为什么说 slice、map、channel 是引用类型?...(b int) { fmt.Printf("%#v\n", &b) // (*int)(0xc420018090) } 注释内容是机器输出,你如果运行会得到不一样输出 根据代码来解释下,所谓值传递就是...) // addr: (**int)(0xc420088028) } 定义了一个变量 a,并把地址保存在指针变量 pa 里边了。...大概意思是说:最开始用指针语法,由于种种原因改成了引用,但是这个引用与C++引用是不同,它是共享关联数据结构。...但是这里有个奇怪现象,大家看到了 arr 地址与 s[0] 有相同地址,这也就是为什么我们在函数内部能够修改 slice 原因,因为当它作为参数传入函数时,虽然 slice 本身是值拷贝,但是它内部引用了对应数组结构

    2.3K20

    Go 函数也是一等公民

    假设,你有一组文件,如下: /bin /pkg /src /src/main.go /src/cell/xx.go 每一个文件里都可以有init函数,但是整个程序结构上,只能有一个main函数。...(init函数可以不写,但main函数必须写) 有一点上,对于函数{},Go有一定规则: func c() { // 这是不合法 } func c(){ // 这是合法 } 在JS里就没有这样规矩...有趣是,在JS中,基础类型都是值传递,只有对象才是引用传递。在Go语言中,如果你不是传递指针,所有的一切都是值传递,也就是说它会copy一份副本。...这一点上,觉得会比JS更有用,因为能很精准去控制,想要值传递还是引用传递,不过在性能上,大块还是传递指针比较好,指针引用着内存地址,这是一个比较方便方式。...: package main import "fmt" func main(){ c(12,a) } func c(num int, f, func(int)){ f(num

    50010

    一个 Java 程序员眼中 Go 语言

    当 Go 编译器发现被创建“对象”(晚点晚再解释用引号原因)将会脱离函数作用域,它会妥善处理这种情况,保证该对象在函数返回后继续存活,其指针不会指向废弃内存地址,获得不确定数据。...在C中,通过结构体时,可以用 b.a 来访问结构体成员;通过结构指针时,可以用 b->a 访问结构体中同一成员。对于指针,试图用 b.a 访问则是语法错误。...——作为一个 Java 开发者,你应该不会觉得奇怪。...我们通过一个 nil 指针调用了方法!这是什么情况? 键入值类型,而非对象。 这就是为什么用引号“对象”。Go保存结构体,其实是内存中一小片区域。...(可能 Perl 是第一个使用这种特性主流类 C 语法语言)既然如此,如果我们必须有花括号,那就没必要用圆括号将条件语句括起来了。

    79830

    C++】类型转换

    return 0; } 但是这里有一个奇怪现象,我们将 a 值和 *p 值打印出来,并且将它们地址打印出来观察: 我们会发现,a 和 p 地址是一样,但是当我们修改 *p...但是我们又发现了另外一个问题,为什么 &a 值是 1 呢?这是因为 cout 对 &a 识别的时候匹配错了,我们只需要将 &a 强转成如下即可: 如果以上转换我们使用C语言强制类型转换可以吗?...那么C++为什么要使用这几种类型转换方式呢?...4. dynamic_cast dynamic_cast 用于将一个父类对象指针/引用转换为子类对象指针或引用(动态转换),这个是C语言不具备。...dynamic_cast 还需要一个前提,就是父类必须要有虚函数。

    11210

    Android native进程间通信实例-binder篇之——解决实际问题inputreader内建类清楚缓存

    在实际开发中,遇到一个问题,在电容屏驱动中没有发送input_sync 给上层,导致电容屏有的数据缓存在inputreader 中,会导致系统一系列奇怪问题发生, 至于为什么驱动不发送input_sync...总之,MultiTouchInputMapper 里面有个重要实现叫做 void MultiTouchInputMapper::reset(nsecs_t when) ,就是它会清空缓存。 c....添加binder 服务 由上面添加clearCTPData 这个接口可知,这个处理是在InputReader 类里面新加一个方法。...调用它就需要有一个指针指向当前InputReader ,好,有了这个想法就开始写代码吧。...首先在InputReader.cpp 中InputReader::InputReader 构造函数中添加咱们binder 指针,binder 调用ontransct类服务也需要重写一下,就命名为MyInputreaderService

    46520

    为实习准备数据结构(1)-- 详尽数组篇

    解释: [在这里插入图片描述] 其实也不知道为什么不把这个问题给办了,所以就参考前边那句话吧,读书少,不要问我。...return 0; } ------ 传递数组给函数 C++ 中,可以通过指定不带索引数组名来传递一个指向数组指针。...C++ 传数组给一个函数,数组类型自动转换为指针类型,因而传实际是地址。...test.at(2); //用这个比直接用下标要好地方在于它会检测越界 (明面上是这么说啊,其实自己写代码也喜欢用下标定位) 头尾指针 这四个函数区别要清楚:begin()、end()、front...喜欢称它们为头尾指针也不知道为什么有人要就这些区别长篇大论。 begin():指向容器一个元素地址。 front():指向容器一个元素值。

    49100

    C++ 虚拟继承

    为什么需要虚继承? 由于C++支持多重继承,那么在这种情况下会出现重复基类这种情况,也就是说可能出现将一个类两次作为基类可能性。比如像下面的情况 ?...因此,它需要多出一个指向基类子对象指针。...这说明:空类所占空间为1,单一继承空类空间也为1,多重继承空类空间还是1.但是虚继承涉及到虚表(虚指针),所以sizeof(C大小为4 相信经过上面的分析和对比,以后看到这类问题不会再疑惑,会有一种...很奇怪吧,为什么是这个结果呢。...主要大小受三个因素影响: 语言本身所造成额外负担,当语言支持虚基类时候,就导致一个额外负担,这个一般都是一个虚表指针。里面存储就是虚基类子对象地址,就是偏移量。

    2.3K80

    Go常见错误集锦之range常踩那些坑

    在该示例中,range循环操作未影响slice原有内容。我们解释下为什么。 因为在Go中,一切赋值操作都是拷贝。...例如,如果我们将函数返回结果赋值给以下变量: 一个结构体,我们得到是这个结构拷贝 一个指针,我们将得到这个指针拷贝( 虽然两个指针变量指向是同一个对象,但仍然是一个指针拷贝) 这点很重要,...实际上,当一个range循环一个数据结构时候,它会对每一个元素拷贝一份,然后赋值给value变量(也就是range中第二个接收变量)。...我们定义一个Customer结构体,来代表一个可用,然后定义一个Store结构体,该结构体包含一个指针类型map,如下: type Customer struct { ID string...range exp中exp可以是string、array、slice、channel,并且在循环开始前,exp只被计算一次,并且循环一个拷贝对象,所以在循环过程中对exp元素进行添加,不会影响到循环次数

    67410

    .NET C# 教程初级篇 1-1 基本数据类型及其存储方式

    本质上就是C++中函数指针。 数组:继承自Array类,属于任意类型一种集合,但不同于集合,大小必须被初始化。在内存中是一段连续内存空间,但是不是值类型。...位(bit) 决定,我们常说一字节在现在计算机中指有8个比特空间大小,一个比特位可以存储一位二进制代码,而我们常见int类型默认是Int32,也就是32位整形,因此你知道为什么int是4个字节了吧...而在大端存储中符号位判定固定为第一个字节,容易判断正负。 为什么要学这个奇怪知识呢?...,由于我们计算机保存数据方式是采取补码存储,因此,当我们对一个负数进行移位时,在添加并不是0而是1。...运算符重载 我们在大部分时候,语言自身提供运算符运算规则已经足够我们使用,但往往我们会涉及到一些奇怪场景,例如我需要知道某两个节日日期相距多少天而我并不想借助DateTime类方法,想用date1

    1.2K30

    Go 切片使用绕坑指南

    不知道大家有没有发现在一个函数内部对切片参数进行了排序后也会改变函数外部原来切片中元素顺序,但是在函数内向切片增加了元素后在函数外原切片却没有新增元素,更奇怪添加并排序后,外部切片有可能元素数量和元素顺序都不会变...为什么? 如前所述,当我们调用 append时,会创建一个切片。...同样,如果我们向s2附加新元素,最终导致其超出支持数组,我们将不再看到对一个切片更改会影响一个切片。 严格来说,这不是一件坏事。...: type slice struct { array unsafe.Pointer len int cap int} 注意到 array字段实际上是一个指针了吗?...同样,你应始终意识到,内部带有指针结构很容易陷入相同情况。除非指针本身被更新为引用内存中一个对象,否则指针内部数据任何更改都将被保留。

    1.2K20

    Golang入门教程——面向对象篇

    它有些类似于C++当中typedef,结合这个含义,我们再来看结构定义就很好理解了。其实是我们通过struct关键字构造了一个结构体,然后使用type关键字定义成了一个类型。...我们在添加方法之前使用type给int起了一个别名,这是因为golang不允许给简单内置类型添加方法,并且接收者类型定义和方法声明必须在同一个包里,我们必须要使用type关键字临时定义一个类型。...我们既然可以定义成普通结构体对象,为什么还要有一个指针对象接收者呢? 其实很好理解, 两者区别有些类似于C++当中值传递和引用传递。...也就是说在golang当中,如果我们函数接收一个指针类型,我们可以在函数内部修改这个结构值。否则的话,传入一个拷贝,我们在其中修改值并不会影响它本身。...如果是指针的话,当我们对结构体值进行修改时候,会影响到原值。即使我们定义接收者类型是指针,我们在调用时候也不必显示将它转化成结构指针,golang当中会自动替我们完成这样转化。

    36820

    CC++基础之sizeof使用

    1 sizeof 定义 sizeof 是 C/C++ 中一个操作符(operator),返回一个对象或者类型所占内存字节数。...: 4 unsigned 不影响内置类型 sizeof 取值 2 指针类型 sizeof 指针主要用于存储地址,前几天文章C语言指针详解提到过,指针变量位宽等于机器字长,机器字长由 CPU 寄存器位数决定...d 是一个奇怪定义,他表示一个指向 double*[3][6] 类型数组指针。既然是指针,所以 sizeof(d) 就是4。...; }; 结构体A和B中包含成员都一样,只不过顺序不同而已,为什么其大小不一样呢?...从三个规则我们来看看为什么 sizeof(B) 等于 24 :首先假设结构首地址为0,第一个成员 num1 首地址是 0 (满足规则2),它类型是 int ,因此它占用地址空间 0——3 。

    35130

    C语言 | C++之sizeof使用

    1 sizeof 定义 sizeof 是 C/C++ 中一个操作符(operator),返回一个对象或者类型所占内存字节数。...: 4 unsigned 不影响内置类型 sizeof 取值 2 指针类型 sizeof 指针主要用于存储地址,前几天文章C语言指针详解提到过,指针变量位宽等于机器字长,机器字长由 CPU 寄存器位数决定...d 是一个奇怪定义,他表示一个指向 double*[3][6] 类型数组指针。既然是指针,所以 sizeof(d) 就是4。...; }; 结构体A和B中包含成员都一样,只不过顺序不同而已,为什么其大小不一样呢?...从三个规则我们来看看为什么 sizeof(B) 等于 24 :首先假设结构首地址为0,第一个成员 num1 首地址是 0 (满足规则2),它类型是 int ,因此它占用地址空间 0——3 。

    2.7K88
    领券