卡卷网
当前位置:卡卷网 / 每日看点 / 正文

如何计算结构体大小?

作者:卡卷网发布时间:2024-12-03 14:59浏览数量:96次评论数量:0次

在C语言中,结构体(struct)和联合体(union) 是常用的复合数据类型,它们的内存布局和字节大小直接影响程序的性能和内存使用。下面为大家详细解释它们的字节大小计算方法,包括对齐规则、内存分配方式及代码示例分析。

结构体(struct)

结构体的定义:

  • 结构体是一种将多个类型的变量组合在一起的复合数据类型,每个成员都有自己的存储空间。
  • 每个成员按顺序存储。
  • 目的:创建新的类型,可面向对象编程,实例化一整个对象 ---> 结构体。

语法:

struct [标签] { 成员属性1; 成员属性2; 成员属性3; 成员属性4; }; // 结构体的关键字 : struct // 标签 可有 可无 // 一个个成员都是 分号 结尾

不带标签的定义:

struct {int a; int b; char c;}; // 有了新的类型 //定义该类型变量: struct {int a; int b; char c;} xx; //取别名: typedef struct {int a; int b; char c;} yy_t; yy_t zz;

带标签的定义:

struct point {int x; int y;} // 创建了新的标签 //定义类型: struct point {int x; int y;} xx; // 创建了point标签,定义xx变量 // 使用标签: struct point sp; // 使用point标签,前提:必须要有point标签 typedef struct point {int x; int y;} qq_t; // 取了个新的别名叫qq_t , 同时 创建了标签 point qq_t x; // 使用新的别名类型 创建变量 x

注意事项:相同名字的标签 不可重复创建

为结构体成员赋值:

struct person { char name[20]; int age; int sg; int tz; int cd; }; struct person sp; // 当你拥有该类型 实例化 对象 ,取里面的成员,用 点(.) strcpy(sp.name,"jack"); sp.age = 18; sp.sg = 180; sp.tz = 180; sp.cd = 18; // 当你知道 该 实例化 对象 的地址的时候,取里面的成员 ,用 箭头 (->) struct person *p = &sp; p->name p->age p->sg p->tz p->cd struct person *xp = (struct person *)malloc(sizeof(struct person)); xp->name xp->age

结构体初始化的三种形式:

struct person { char name[20]; int age; int sg; int tz; int cd; }; //第一种: struct person sp1 = {"jack",18, 180}; struct person sp2 = {"jack",18, 180, 180, 18}; //第二种:(内核中结构体的初始化方式) struct person sp3 = { .name = "jack", .cd = 18, .sg = 180 }; //第三种: struct person sp4 = { name:"jack", cd : 18, sg :180 };

内存布局

字节对齐(Alignment):

  • 在分配内存时,编译器会根据硬件架构的要求进行对齐(Alignment),确保变量的地址是其类型所需的对齐倍数。
  • 对齐规则:
1. 每个成员的起始地址必须是其自身类型对齐要求的倍数。
2. 结构体的总大小必须是最大对齐要求的倍数。

计算结构体的字节数:

> 结构体最终的字节数一定是 对齐字节数的 整数倍
> 在32操作系统中,最大4字节对齐
> 在64操作系统中,最大8字节对齐
> 取 结构体中 最大的 那个成员基本组成类型字节数 作为 对齐字节数
> 每个成员的起始位置到结构体的起始位置之间的字节数 是该成员基本组成字节数 的整数倍

影响结构体大小的因素:

成员类型的对齐

  • - 不同数据类型的对齐要求可能不同(通常与数据类型的大小一致)。
  • - 示例(32位系统):
  • - char:1字节对齐。
  • - short:2字节对齐。
  • - intfloat:4字节对齐。
  • - double:8字节对齐。

编译器的填充(Padding)

  • - 为了满足对齐要求,结构体成员之间可能会插入填充字节(Padding)。
  • - 结构体的总大小也会填充到最大对齐要求的倍数。

最大对齐要求

  • - 结构体总大小必须是其最大成员对齐要求的倍数。

内存布局详情:

struct xx { long long ll; char c; int *p; char name[21]; short s; double d; float f; short x; };

如何计算结构体大小?  第1张

64位系统下结构体的字节数

如何计算结构体大小?  第2张

32位系统下的结构体

如何计算结构体大小?  第3张

Ubantu 32位系统下的结构体字节数

也可以人为设定对齐字节数

在32操作系统中,最大4字节对齐
在64操作系统中,最大8字节对齐
#pragma pack(n) n: 是2的幂次方 2^0 2^1 2^2 2^3

代码示例:

1:无填充字节

#include <stdio.h> struct S1 { char a; // 1字节 int b; // 4字节 }; int main() { printf("Size of S1: %lu\n", sizeof(struct S1)); return 0; }

内存布局:

+---+---+---+---+ | a | P | P | P | // char a (1字节) + 3字节填充 +---+---+---+---+ | b | // int b (4字节) +---+---+---+---+

  • char a 占 1 字节,但 int b 需要 4 字节对齐,因此插入 3 字节填充。
  • 总大小为 8 字节。

Size of S1: 8

2:多个对齐要求

#include <stdio.h> struct S2 { char a; // 1字节 double b; // 8字节 int c; // 4字节 }; int main() { printf("Size of S2: %lu\n", sizeof(struct S2)); return 0; }

内存布局:

+---+---+---+---+---+---+---+---+ | a | P | P | P | P | P | P | P | // char a + 7字节填充 +---+---+---+---+---+---+---+---+ | b | // double b (8字节) +---+---+---+---+---+---+---+---+ | c | P | P | // int c (4字节) + 4字节填充 +---+---+---+---+---+---+---+---+

  • char a 占 1 字节,double b 需要 8 字节对齐,因此插入 7 字节填充。
  • int c 需要 4 字节对齐,但结构体总大小需为 8 的倍数,因此插入 4 字节填充。
  • 总大小为 24 字节。

Size of S2: 24

3:改变成员顺序减少填充

调整示例 2 的成员顺序:

struct S3 { double b; // 8字节 int c; // 4字节 char a; // 1字节 };

内存布局:

+---+---+---+---+---+---+---+---+ | b | // double b (8字节) +---+---+---+---+---+---+---+---+ | c | a | P | // int c + char a + 3字节填充 +---+---+---+---+---+---+---+---+

  • 调整成员顺序后,总大小为 16 字节,减少了填充字节。

输出:

Size of S3: 16

4. 结构体嵌套的字节大小

嵌套结构体会继承成员的对齐规则,可能导致额外的填充字节。(此处仅做简单介绍,详细分析请跳转至

C语言能否通过结构体实现面向对象编程?

回答,对结构体的嵌套有详细说明)

代码

#include <stdio.h> struct Inner { char a; // 1字节 int b; // 4字节 }; struct Outer { char c; // 1字节 struct Inner inner; // 8字节(受对齐规则影响) double d; // 8字节 }; int main() { printf("Size of Inner: %lu\n", sizeof(struct Inner)); printf("Size of Outer: %lu\n", sizeof(struct Outer)); return 0; }

内存布局:

Inner: +---+---+---+---+---+ | a | P | b | +---+---+---+---+---+ Outer: +---+---+---+---+---+---+---+---+ | c | P | Inner (8字节) | +---+---+---+---+---+---+---+---+ | d | +---+---+---+---+---+---+---+---+

Inner 的大小为 8 字节(对齐到 4 的倍数)。
Outer 的最大对齐要求是 double d 的 8 字节,总大小为 24 字节。

输出:

Size of Inner: 8 Size of Outer: 24


联合体(union)

联合体的定义:

  • 联合体的所有成员共享同一块内存空间。
  • 联合体的大小取决于其最大成员的大小。
  • 联合体的对齐要求由最大成员的对齐要求决定。

语法:

union [标签] { 成员属性; 成员属性; 成员属性; 成员属性; };

定义:

union { int a; short b; char c; } // 联合体 类型 typedef union {int a; short b; char c;} xx_t;//取别名 xx_t y; union qq{int a; short b; char c;} // 有了新的联合体类型,并创建了新的标签 union qq xq;

赋值:

typedef union {int a; short b; char c;} xx_t; xx_t y; y.a = 10; //同结构体一样 (&y)->b = 20;

联合体的存储(字节数如何计算):

  • 占用空间最大的成员字节数作为基础字节数。
  • 最终的字节数一定是 每个成员基本组成类型字节数的整数倍。

union xx { char name[20]; long long ll; double d; int *p; short s; };

示例代码中联合体的大小取决于其最大成员 char name[20];,所以大小为20字节。

如何计算结构体大小?  第4张

代码:

1:简单联合体

#include <stdio.h> union U1 { char a; // 1字节 int b; // 4字节 double c; // 8字节 }; int main() { printf("Size of U1: %lu\n", sizeof(union U1)); return 0; }


+---+---+---+---+---+---+---+---+ | c | // double c (8字节) +---+---+---+---+---+---+---+---+

  • 这个联合体的大小取决于其最大成员 double c,所以大小为 8 字节。

输出:

Size of U1: 8

2:联合体的对齐

#include <stdio.h> union U2 { char a; // 1字节 int b; // 4字节 short c; // 2字节 }; int main() { printf("Size of U2: %lu\n", sizeof(union U2)); return 0; }

内存布局:

+---+---+---+---+ | b | // int b (4字节) +---+---+---+---+

  • 联合体的最大成员是 int b(4 字节),所以联合体的大小为 4 字节。

输出:

Size of U2: 4


枚举类型(enum)

定义:枚举类型(enum)是C语言中定义一组**命名整型常量**的方法,用于表示一组离散的、有意义的值。 它是C语言的基本类型之一,主要用于提高代码的可读性和可维护性。

定义有些抽象,不太易于理解,用大白话翻译一下就是:① 结构体是把不同的成员捆绑在一起,形成一个新的类型,方便于面向对象编程,适用于实例化对象使用。② 联合体也是创建一个新的类型,只不过它的成员是共用一块空间,而结构体成员是分别使用空间。③ 枚举类型则是用于限制取值范围的。

语法:

enum [标签] { 枚举常量, 枚举常量, 枚举常量, 枚举常量, 枚举常量 };

  • 枚举常量: 枚举常量是int型的常量,在使用int类型的任何地方都可以使用它。
  • 默认值: 没有特定指出常量值时,枚举列表中的常量被指定为整数值0、1、2等,依次递增。
  • 指定值: 可以选择常量具有的整数值,后面的常量会被赋予后续的值。

定义:

enum {XX,YY,ZZ,QQ,TT} // 4个字节 enum {XX,YY,ZZ,QQ,TT} xy = XX; typedef enum {XX,YY,ZZ,QQ,TT} hh_t; 带标签: enum Weekday {MON, TUE, WEN, THU, FRI,SAT, SUN}; MON : 0 ---> 依次往后加1 //枚举常量的值 下面的代码块中会进一步详细说明 enum Weekday {MON=10,TUE, WEN, THU=100, FRI,SAT, SUN}; //这个类型 4个字节 enum Weekday yy = WEN;//赋值:只能在枚举类型中限定的范围内查找 //枚举和 typedef:通过 typedef 为枚举类型定义别名,简化代码 typedef enum Weekday {MON=10,TUE, WEN, THU=100, FRI,SAT, SUN} week_t; void fun(week_t xx) { } fun(MON);//从此之后,此函数中的传值被限制了范围,只能在 enum Weekday中查找

写一个示例代码,详细讲述一下枚举类型的使用场景:

enum 枚举类型名 { 枚举常量1, 枚举常量2, 枚举常量3, ... };

  • enum:枚举类型的关键字。
  • 枚举类型名/[标签]:可选,表示该枚举的类型名称。
  • 枚举常量:一组用逗号分隔的命名常量,默认从 0 开始递增(可以手动指定值)。

使用:

#include <stdio.h> // 定义枚举类型 enum Color { RED, // 默认值 0 GREEN, // 默认值 1 BLUE // 默认值 2 }; int main() { enum Color myColor; // 声明枚举变量 myColor = GREEN; // 给枚举变量赋值 printf("My color is: %d\n", myColor); // 输出枚举变量的值 return 0; }

输出:

My color is: 1


枚举常量的值

默认值

  • 枚举常量的值从 0 开始,依次递增。
  • 如果手动为某个枚举常量指定值,则后续常量的值在此基础上递增。

手动指定值

#include <stdio.h> // 定义枚举类型,并手动指定值 enum Weekday { MONDAY = 1, // 指定为 1 TUESDAY, // 自动递增为 2 WEDNESDAY = 5, // 指定为 5 THURSDAY, // 自动递增为 6 FRIDAY // 自动递增为 7 }; int main() { printf("MONDAY = %d\n", MONDAY); printf("TUESDAY = %d\n", TUESDAY); printf("WEDNESDAY = %d\n", WEDNESDAY); printf("THURSDAY = %d\n", THURSDAY); printf("FRIDAY = %d\n", FRIDAY); return 0; }

输出:

MONDAY = 1 TUESDAY = 2 WEDNESDAY = 5 THURSDAY = 6 FRIDAY = 7

枚举值的重复: 枚举常量可以有重复的值。

enum Example { A = 1, B = 1, // B 和 A 的值相同 C = 2 };


匿名枚举

  • 如果不需要为枚举类型定义名字,可以使用匿名枚举。
  • 枚举常量会成为全局常量,直接使用。

代码

#include <stdio.h> enum { SUCCESS = 0, FAILURE = -1 }; int main() { printf("SUCCESS = %d\n", SUCCESS); printf("FAILURE = %d\n", FAILURE); return 0; }

输出:

SUCCESS = 0 FAILURE = -1


枚举类型的大小

  • 枚举变量的本质:
  • - 枚举类型在底层是一个整数类型,通常是 int(具体大小取决于编译器)。

内存大小:

  • - 一个枚举变量的大小与 int 相同(4字节或8字节,取决于平台)。

代码

#include <stdio.h> enum Example { A = 1, B, C }; int main() { printf("Size of enum: %lu bytes\n", sizeof(enum Example)); return 0; }

输出(在 64 位系统上):

Size of enum: 4 bytes

枚举类型的限制和注意事项

枚举变量的类型:

  • 枚举类型的底层是整型,因此可以与整数进行比较或赋值。
  • 但这种用法可能会降低代码的可读性。

代码说明

#include <stdio.h> enum Color { RED, GREEN, BLUE }; int main() { enum Color myColor; myColor = 1; // 虽然可以赋值为整数,但不推荐 printf("My color is: %d\n", myColor); return 0; }

输出:

My color is: 1 //注意:虽然语法上允许,但这样会破坏枚举的意义。建议始终使用枚举常量赋值。

枚举类型的范围: 枚举常量的值不能超过整型的表示范围,其本质是个 `int` 常量。如果枚举值太大,可能会导致编译器警告或错误。

枚举和类型安全: C语言中的枚举并不强制类型安全。枚举变量可以被赋值为任何整数值,编译器不会报错。


枚举类型的作用

1. 提高可读性:枚举类型使用有意义的命名代替魔法数字(Magic Numbers)。例如,使用 RED 代替数字 0,提高代码可读性。

2. 提高可维护性:如果枚举的值发生变化,只需修改枚举定义,而不用修改其他代码。

3. 限制变量取值范围:枚举变量只能取定义的枚举常量值,避免了非法值的使用。

枚举的高级用法

最后举 2 种枚举类型在编程中的常用场景:

1. 枚举结合 switch 语句使用: 枚举常常与 switch 语句结合使用,方便处理多个分支逻辑。

代码片:

#include <stdio.h> enum State { OFF, ON, SUSPEND }; int main() { enum State deviceState = ON; switch (deviceState) { case OFF: printf("Device is OFF.\n"); break; case ON: printf("Device is ON.\n"); break; case SUSPEND: printf("Device is in SUSPEND mode.\n"); break; default: printf("Unknown state.\n"); } return 0; }

输出:

Device is ON.

2. 枚举用于位标志: 枚举可以用于定义位标志,方便对多个状态进行位操作。

代码片:

#include <stdio.h> enum Permission { READ = 1 << 0, // 0001 WRITE = 1 << 1, // 0010 EXECUTE = 1 << 2 // 0100 }; int main() { int permission = READ | WRITE; // 组合权限 if (permission & READ) { printf("Read permission is granted.\n"); } if (permission & WRITE) { printf("Write permission is granted.\n"); } if (permission & EXECUTE) { printf("Execute permission is granted.\n"); } return 0; }

输出:

Read permission is granted. Write permission is granted.

综上。需要简单记住的是:

1. 结构体大小计算规则

  1. 每个成员按其对齐要求分配地址。
  2. 结构体总大小对齐到最大对齐要求的倍数。
  3. 填充(Padding)字节会增加结构体大小。

2 联合体大小计算规则

  1. 联合体的大小由最大成员的大小决定。
  2. 联合体的对齐要求由最大成员的对齐要求决定。

3. 枚举类型大小计算规则

  1. 枚举类型在底层是一个整数类型,通常是 int(具体大小取决于编译器)。
  2. 内存大小:一个枚举变量的大小与 int 相同(4字节或8字节,取决于平台)。

可优化建议

  1. 调整成员顺序:将对齐要求高的成员放在前面,减少填充字节。
  2. 使用 #pragma pack:可以改变默认对齐方式,但可能影响性能。
  3. 注意嵌套结构体的对齐:嵌套结构体的总大小也需满足对齐规则。

通过理解这些规则,可以优化结构体和联合体的内存布局,从而提高程序的性能和内存利用率。

此篇文章仅仅只是对结构体、联合体、枚举类型的基础教育,简单讲解了它们是如何占用内存空间大小的,下一篇文章中会更进一步的讲解说明;

包含:结构体嵌套以及实际开发中的使用,位域、无名位域和空域详解并包含嵌入式开发工作中实际场景说明:

结构体中位域是如何实现的,比如位域影响结构的内存布局、位域的有效位的摆放、和存取位域的方式?

以上。仅供学习与分享交流,请勿用于商业用途!转载需提前说明。

END

免责声明:本文由卡卷网编辑并发布,但不代表本站的观点和立场,只提供分享给大家。

卡卷网

卡卷网 主页 联系他吧

请记住:卡卷网 Www.Kajuan.Net

欢迎 发表评论:

请填写验证码