欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > 自定义类型:结构体

自定义类型:结构体

2025/9/18 20:50:45 来源:https://blog.csdn.net/m0_62048999/article/details/141227268  浏览:    关键词:自定义类型:结构体

一、结构体类型的声明

1、结构体的声明

结构体的声明:
struct tag
{member-list
};

2、结构体变量的创建和初始化

创建一个简单的学生信息结构体

struct student
{char name[20];int age;int score;
};

结构体默认初始化的顺序是结构体中创建变量的顺序。

初始化结构体:

struct student
{char name[20];int age;int score;
}s1, s2 = {"李华",18,88};  //第一种初始化struct student s3 = { "小明",20,100 };   //第二种初始化int main()
{struct student s4 = { "张三",15,70 };    //第三种初始化struct student s5 = { .score = 99,.name = "小红",.age = 22 };  //第四种初始化return 0;
}

3、特殊声明

匿名结构体

struct 
{int a;char b;float c;
}x,*p;

匿名结构体只允许使用一次,p=&a是非法请求

4、结构体的自引用

结构体是可以进行自引用的

错误的自引用:
struct stu
{int a;struct stu next;
};
正确的自引用:
struct stu
{int a;struct stu* next;
};

5、typedef重命名结构体

typedef struct Node
{int data;struct Node* next;
}Node;   //结构体命名为Node

二、结构体内存对⻬

1、对齐规则

掌握结构体的对齐规则:

  1. 结构体中第一个变量首对齐地址是偏移量为0的地址处。
  2. 其他成员的对齐变量是类型的整数倍数的地址处。vs默认对齐量为8,linux中gcc没用对齐数,对齐数就是自身大小
  3. 结构体的总大小为最大对齐数
  4. 对于嵌套结构体,嵌套结构中对齐量是结构体中最大的一个变量整数倍数地址处,而结束时是嵌套结构体的整数倍数

如计算int 对齐数 4 8 4 int类型的大小对比默认值大小,谁小选谁是对齐数

//练习1
struct S1
{char c1;int i;char c2;
};
printf("%d\n", sizeof(struct S1));
//练习2
struct S2
{char c1;char c2;int i;
};
printf("%d\n", sizeof(struct S2));
//练习3
struct S3
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S3));
//练习4-结构体嵌套问题
struct S4
{char c1;struct S3 s3;double d;
};
printf("%d\n", sizeof(struct S4));

在以上例题中,我们发现结构体中开辟的空间有许多空间被浪费了,所以在创建类型时,需要合理的排序

在例题1与例题2中,创建的类型是一样的,就因为类型创建的顺序不一样,导致占用内存不一样。

尽量使变量小的类型排在前面

2、为什么存在内存对齐

1、平台移植问题:不是所有的硬件平台都能访问任意地址上的任意数据

2、性能问题:如果没用地址对齐,处理器需要作两次的内存访问,而对齐的内存只需要访问一次

总结来说:拿空间换时间

3、修改默认对齐数

通过使用#pragma这个预处理指令,可以改变编译器的默认对齐数

#pragma pack(4)//设置对齐数变为4
struct stu
{char a;int b;char d;double c;
};
#pragma pack()//取消设置的对齐数,还原默认
int main()
{printf("%zd\n", sizeof(struct stu));
}

三、结构体传参

struct stu
{char name[10];int age;
};struct stu s1 = { "xiaoming",28 };void print1(struct stu s1)
{printf("%d,%s\n", s1.age, s1.name);
}void print2(struct stu* s1)
{printf("%d,%s\n", s1->age, s1->name);
}
int main()
{print1(s1);   //传值调用print2(&s1);  //传址调用return 0;
}

输出:

结构体可以使用传值也可以使用传址,优先选用传址调用,这样不需要开辟新的空间,可以大大提高代码的运行效率。

四、结构体实现位段

1、什么是位段

位端可以限制结构体中创建变量占用内存的大小

  1. 使用位段时,成名必须是unsigend int,int,sigent int,在c99中其他成员可以使用位段
  2. 位段的创建在成员名后面加上一个冒号跟一个数字。

比如:

struct stu
{int _a : 4;int _b : 2;int _c : 1;int _d : 30;
};

2、位段的内存分配

  1. 位段的成员可以是int,unsigent int ,signet int,char类型
  2. 位段上的空间是以四个字节跟一个字节进行开辟空间的
  3. 位段无法跨平台使用
struct stu
{int _a : 4;int _b : 10;int _c : 1;int _d : 20;
};int main()
{printf("%zd\n", sizeof(struct stu));return;
}

输出:

如果没用位段,那么4个int类型的字节大小位12

位段是如何开辟空间的,如下:

3、位段的跨平台问题

1、int位段是被当成有符号位还是无符号位是不确定的

2、位段中最大数是不确定的(有16位机器,如果设置段位超过16比特,那么就会出问题,32位机器同意也是)

3、内存是从左到右分配,还是从右到左分配是不确定的(vs是从右到左分配)

4、当一个结构体包含两个位段时,第二个位段成员比较大,无法容纳第一个尾端剩余空间的时候,是否会舍弃剩余空间,这是不确定的。

总结:位段可以很好的节约空间,但存在跨平台问题

4、位段的应⽤

下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要⼏个bit位就能描述,这⾥使⽤位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报⼤⼩也会较⼩⼀些,对⽹络的畅通是有帮助的。

5、位段使⽤的注意事项

无法获取比特位的地址。

无法直接使用scanf来输入位段,但是可以赋值位段

struct stu
{int _a : 4;int _b : 10;int _c : 1;int _d : 20;
};int main()
{struct stu s = { 0 };int n = 0;scanf("%d", &n);s._a = n;return;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词