自定义结构:结构体

你好,这是一篇关于结构体的文章,适合 C语言萌新、小白来阅读。

我这条懒狗终于爬起来更新了!!!好了废话不多说,直接进入正题

一、结构体的定义

结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。

它的基本语法结构是这样的:

struct tag
{
    number.list

}variable.list;


//比如,我们可以创建一个学生的结构体
struct student
{
char name[20];
char sex[4];
int age;
int identity_number[20];
int sch_num[20];
int grade;
}s1,s2;
//注意,此处也可以直接对s1和s2里面的成员赋值
即
}s1={"王亮","男",19,1234567891011121314,23001452214,120},s2;

生活当中有很多数据需要保存下来,如果你是一个学生,你需要记住自己的姓名,年龄,性别,身份证号,学号,甚至一些重要的分数,诚然,我们可以通过创建变量的形式把这些数据存起来,如果只存一个学生的这些数据,那我们创建六个变量就行了,可是如果要存储一堆学生的数据,那重复创建类型相同变量的次数太多了,我们就可以使用结构体来精简化我们的代码。

1.1 匿名结构体

就是不给他命名的结构体。tag

注意:匿名结构体基本上只能够调用一次,就算你创建两个一模一样的匿名结构体,编译器也会把他们当作不同的结构体。

struct 
{
member.list
}variable.list;

二、结构体变量的创建

如果我们想要创建一个结构体变量,就可以这样进行操作,还是以上面创建的结构体为例。

struct student s3;//此处可以初始化,也可以不初始化

三、结构体的引用

这块与链表知识是有联系的。

首先,结构体里面还可以放结构体吗?

3.1 结构体的互引用

这个问题就像在问函数能不能在函数内部调用另一个函数一样,答案也很明显:是可以的!

struct a1
{
int a;
int b;
int c;
char d;
};

struct a2
{
int a;
int b;
struct a1 p;
};

那,我能在结构体里面放一个和现在这个结构体一样的结构体吗?应该说就是同一个,答案也很明显:也是可以的!

3.2 结构体的自引用

当然你肯定已经蠢蠢欲动了,甚至已经脑补出了代码的样子

struct a1
{
int a;
struct a1 a;
};

但是我要悲痛地告诉你一个坏消息,这么写是错误的!

这个东西会报错的!!!!为什么呢?看英文:'a' has incomplete type 'a1'(不要问我为什么a2改成a了)

也就是说a1是未完成的。

在结构体内部定义结构体变量时,它会去找这个结构体,但该结构体还未声明完成,所以无法被引用和定义。

当然还有一种解释,就是你这样写结构体里面嵌套自己,那就会反复套娃,里面存储的数据量会是无穷大的,这一点也不合理。

所以,正确的写法是什么捏?

struct a1
{
char c;
int a;
struct a1 *p;
};

是的,加上一个星号就行。星号是干什么用的?把*p理解为指针就行了,p是指针变量,里面存地址,*是解引用操作符,顺着p里面地址就找到了值。

typedef不要和匿名结构体一起用!!!

四、结构体的内存对齐

好小子,看的真快,这部分内容其实也还好啦,不难,你信我!

4.1 对齐规则

1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
        对⻬数=编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。

注意:VS 中默认的值为 8,Linux中gcc没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的
整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构
体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

好的你已经掌握了这部分内容,接下来让我们开始做题吧!

题目一
#include <stdio.h>
struct a
{
char a1
int a3;
char a2;
};

int main()
{
printf("%d",sizeof(struct a);
return 0;
}

如果你刚才跳过了标黄的部分,你会不会下意识以为屏幕会打印6?事实上我一开始就是这样的=-=

别学我嗷,一定要认真听课,废话不多说,就拿vs环境下运行来举例好了

首先,地址从0开始的,我们知道int 数据大小是四个字节(32bit),char数据大小是一个字节(8bit),好的,那默认对齐数又根据上面标黄部分可以知道是8了,对齐数对int而言就是4,对char而言就是1了。

那接下来工作就简单了,拿出笔和纸和我一起算=-=

你只要知道int对着那个数的必须是4的倍数就行,因为它的对齐数是4,如果不是就浪费掉。char同理。

结构体大小是最大对齐数整数倍。。9肯定不是4的倍数,那就取12.

答案就是12个字节,你算对了吗?

题目二
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));

变量换了个位置,结果让人吃惊=-=

这个8个字节没问题

题目三
struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));

16个,各位照着上面依葫芦画瓢即可

题目四
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));

32个

4.2 为什么存在内存对⻬?

1. 平台原因(移植原因):
  不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因:
  数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
   总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

4.3 修改默认对齐数

用#pragma来实现

#pragma pack(8);//设定默认对齐数为8

#pragma pack();//恢复对齐数

五、位段

5.1 什么是位段?

位段(bit-field)是以位为单位来定义结构体(或联合体)中的成员变量所占的空间。含有位段的结构体(联合体)称为位段结构。采用位段结构既能够节省空间,又方便于操作。

注意:1.位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以
选择其他类型。

           2.位段的成员名后边有⼀个冒号和⼀个数字。

应该怎么创建一个位段呢?

我们来看下面一段代码

struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};

就是_a这个变量只会用两个比特位来存数据,_b,_c,_d都是这样

这个结构体的大小是ceil((2+5+10+30)/8),即6个字节

5.2 位段的内存分配

1. 位段的成员可以是 int ,unsigned int , signed int 或者是 char 等类型。
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。

好我们来看一道题

题目一
//⼀个例⼦
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

希望大家能够理解=-=

5.3 位段的跨平台问题

1. int位段被当成有符号数还是⽆符号数是不确定的。
2. 位段中最⼤位的数⽬不能确定。16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会
出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃
剩余的位还是利⽤,这是不确定的。

总结:
跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

5.4 位段使用注意事项

位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。

  • 12
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值