自定义类型之结构体,枚举,联合(上)

简介: 自定义类型之结构体,枚举,联合

一、结构体


在初识结构体一文中,我们对结构体已经有所了解.


1.结构体的声明,


2.结构体变量的定义与初始化,


3.结构体传参.


其实结构体是一个很重要的内容,在数据结构中,应用十分广泛.所以学好结构体也是十分重要的!


1.1 匿名结构体


匿名结构体是一种省略struct后面的类型名的一种结构体类型.


这种情况好少见,是我们需要使用这个结构体一次时,后面不会再用这个结构体时使用.(个人感觉用处不大).


是一种不完全结构体的声明.


struct
{
  int age;//年龄
  char name[10];//姓名
  float grade;//成绩
}s1 = { 18,"初阶牛",80.0f };
int main()
{
  printf("%d %s %f\n", s1.age,s1.name,s1.grade);
  return 0;
}


注意匿名结构体只有在声明结构体时的后面定义变量,否则对于一个没有名字的结构体,下次想使用就很难找到它了.


补充知识:


两个拥有相同成员变量的结构体,他们是同一类型的结构体吗?


对于两个拥有相同成员变量的结构体,编译器并不会将他们视作同一结构体类型.


//匿名结构体类型
struct s1
{
  int age;//年龄
  char name[10];//姓名
  double grade;//成绩
}s1 = { 18,"初阶牛",50 };
struct s2
{
  int age;//年龄
  char name[10];//姓名
  double grade;//成绩
}  *p;
int main()
{
  p = &s1;
  printf("%d %s %lf\n", p->age, p->name, p->grade);
  return 0;
}


运行结果:


18 初阶牛 50.000000


这里之所以能打印出来是因为,在c语言中会发生强制转换,p会指向s1的地址,刚好偏移量又一样,所以才会打印出来,但是这并不意味着这两个结构体类型相同.


1.2 结构体的自引用


之前我们见过结构体的嵌套定义.


如:


#include <stdio.h>
typedef struct teacher
{
  char name[20];//名字
  char subject[20];//科目
}ter;//将struct teacher重命名为ter,使得类型名更加简洁
typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
  ter t1;//嵌套定义,一个结构体中包含令一个结构体.
}stu;//同理struct student重命名为stu
int main()
{
  //嵌套结构体的初始化
  stu s1 = { "初阶牛",20,"男","20216666",{"pengge","c语言"} };
  //嵌套结构体的打印
  printf("%-6s %-2d %s %s %s %s\n", s1.name, s1.age, s1.sex, s1.id, s1.t1.name, s1.t1.subject);
  return 0;
}


嵌套定义示例图解:



那么可以自己引用自己(自引用)吗?


我们试着尝试写一下代码:


错误示例1:


typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  stu s1;
}stu;


原因:


虽然我们对struct student类型进行了重命名为stu,但是在结构体内部还没有生效,因为这个结构体类型的定义还没结束,结构体本身并不认识stu,只有声明结束后才可以使用.


typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  struct student s1;
}stu;


原因:


虽然我们这里正确的使用了结构体的类型名,但是,如果一个结构体中引用了自己,那么这个结构体的大小是多少?这不就无限递归了吗?


这样?



正确的自引用方法是:


typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  struct student* next;//一个结构体指针,用于指向与自己类型相同的结构体
}stu;


这种结构体自引用的情况在数据结构的链表中就有应用.


我们可以简单了解一下,后续在数据结构中会详细讲解.


这只是申请了局部变量作为链表的成员,仅仅只是为了介绍结构体自引用的应用.


不需要过分研究.


typedef struct student
{
  char name[20];//名字
  int age;//年龄
  char sex[5];//性别
  struct student* next;
}stu;
int main()
{ 
  stu s1 = { "学生1",18,"男",NULL };//暂时将最后一个成员结构体指针置空
  stu s2 = { "学生2",19,"女",NULL };
  stu s3 = { "学生3",20,"男",NULL };
  //将这些结构体都连接起来
  s1.next = &s2;
  s2.next = &s3;
  //创建一个头指针来访问他们
  stu* head = &s1;
  //打印
  while(head != NULL)//最后一个结构体的next是NULL
  {
    printf("%-8s %-5d%s\n", head->name, head->age, head->sex);
    head = head->next;//往后找下一个结构体
  }
  return 0;
}


运行结果:


学生1 18 男


学生2 19 女


学生3 20 男


链表的简单了解:


抽象图:



内存中的存储图解:



1.3 结构体内存大小的计算


结构体大小的计算是一个重要知识点.


试着猜一下结构体stu的大小是多少?


示例1:


struct student
{
  int a;
  char b;
  int c;
}stu;
int main()
{
  printf("%zd", sizeof(stu));
  return 0;
}


12


不知道友友,们有没有听过内存对齐.


其实结构体可是一个纨绔子弟,"富哥"都是很奢侈的,他经常浪费内存!!!


结构体大小计算方法:


内存对齐规则:


  1. 第一个成员在与结构体变量偏移量为0的地址处。从偏移量为0的地址处向后使用.


  1. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。


对齐数 =编译器的默认对齐数与变量成员大小中的较小值.(在VS中默认对齐数是8)


  1. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。


  1. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的成员的对齐数)的整数倍。



还是举例子更好理解对吧?


🌰栗子


示例1:普通结构体


示例1答案:


首先我们将首地址作为偏移量为0的地址.


第一个元素是整形,占4个字节,默认对齐数是8字节,较少者是4字节,则对齐数就是4,故偏移量0-3分配给a变量.


第二个元素是char类型,占1个字节,min(1,8),则对齐数是1,故偏移量4位置分配给b变量.


第三个元素是int型,同理,默认对齐数是4,则偏移量5-7都不能使用,是的你没有听错,这三个字节都被浪费掉了,从偏移量为8开始,8-11偏移的地址分配给变量c.


最后,此时占用了12个字节,最大的成员对齐数是4,刚好12是4的倍数,所以整个结构体的大小就是占12个字节.


图解:



示例2:包含double类型成员的结构体


struct S4
{
  double d;
  char c;
};


运行结果:


16


原因:


double占八个字节,则0-7的偏移量分配给d


char占一个字节,则偏移量8的位置分配给c


总字节数为9,但是成员最大对齐数是8,9不是8 的倍数,所以需要内存对齐,故最后占16字节.


图解:



将结构体改成如下结构,一样是占用16字节.


struct S4
{
  double d;//0-7
  char c;//8
  int i;//12-15
};


示例3:嵌套结构体的内存大小计算


struct S3
{
  int a;
  char c1;
  int i;
};
struct S4
{
  char c1;
  struct S3 s3;
  int d;
};
int main()
{
  printf("%d\n", sizeof(struct S4));
  return 0;
}


运行结果:


20


图解:



示例四:包含数组的结构体


#include <stdio.h>
typedef struct student
{
  char a;
  int arr[6];
  char b;
}stu;
int main()
{
  printf("%d", sizeof(stu));
  return 0;
}


数组就将其看成该元素类型的多个成员变量即可,即对齐数是4,就是6个int型的变量。


图解:



练习题1:


#include <stdio.h>
struct S2
{
  char c1;    //0
  int i;      //4-7
  char c2;    //8
};
struct S3
{
  char c1;    //0
  char c2;    //1
  int i;      //4-7
};
int main()
{
  struct S2 a = { 'a',1,'b' };
  struct S3 b = { 'a','b',1};
  printf("%d\n", sizeof(struct S2));  //12
  printf("%d\n", sizeof(struct S3));  //8
  return 0;
}


当我们拿捏不定时,我们可以通过调试,在内存窗口观察其中的值.


补充知识:cc是系统分配空间时初始化的值,我们就理解为未知值(未被使用)


字符a的ASCII码值是97(十进制)---->61(16进制).


字符b的ASCII码值是98(十进制)---->62(16进制).


s2在内存中:



s3在内存中



练习2:


struct S3
{
  int a;//0-3
  char c1;//4
  int i;//8-11
  double b;//16-23
};
struct S4
{
  char c1;//1
  struct S3 s3;//8-31
  int d;//32-35
};
int main()
{
  printf("%d\n", sizeof(struct S4));
  return 0;
}


答案:40

目录
相关文章
|
4月前
|
Linux C语言 C++
自定义类型——结构体、枚举和联合
自定义类型——结构体、枚举和联合
|
2月前
|
编译器
自定义类型:联合和枚举
自定义类型:联合和枚举
24 0
|
7月前
|
存储 编译器 Linux
自定义数据类型:结构体+枚举+联合
自定义数据类型:结构体+枚举+联合
|
存储 编译器 C语言
自定义数据类型:结构体,枚举,联合
自定义数据类型:结构体,枚举,联合
|
存储 编译器 Linux
自定义类型——结构体,枚举,联合
自定义类型——结构体,枚举,联合
|
编译器 C++
【学习笔记之我要C】自定义类型详解(结构体+枚举+联合)
【学习笔记之我要C】自定义类型详解(结构体+枚举+联合)
298 0
|
编译器 C++
自定义类型:结构体,枚举,联合 (1)
自定义类型:结构体,枚举,联合 (1)
72 1
|
编译器 C++
自定义类型:结构体,枚举,联合
自定义类型:结构体,枚举,联合
|
存储 编译器 C++
自定义类型:结构体,枚举,联合 (2)
自定义类型:结构体,枚举,联合 (2)
67 0
|
存储 安全 编译器
自定义类型:结构体,枚举,联合
自定义类型:结构体,枚举,联合
115 0