C语言中的自定义类型

本文介绍了C语言中的三种自定义类型:结构体、联合体和枚举。详细讲解了它们的声明、创建、初始化、内存对齐以及在函数传参中的使用,强调了内存对齐的重要性并展示了联合体和枚举的特性。
摘要由CSDN通过智能技术生成

在C语言中有三种常见的自定义类型:结构体,联合体,枚举

1.1  结构体

1.1.1 结构体的声明

struct tag{

    member-list; //成员清单

}variable-list;  //变量清单

例如:我们创建一个结构体的变量,来描述一个学生。

struct Student{
    char name[20];
    int age;
    char sex[5];
    char id[20];
};

1.1.2 结构体的创建与初始化

#include<stdio.h>

struct Student {
    char name[20];
    int age;
    char sex[5];
    char id[20]; 
};

int main(){
   struct Student s1={"张三",18,"男","2110000"};//按照顺序初始化
   struct Student s2={.name="李四",.id="9850000",.age=18,.sex="女"};
  //不按照顺序初始化
    
   return 0;
}

1.1.3 结构体的不完全初始化

    我们在创建一个结构体的时候,允许对结构体进行不完全的初始化。省掉结构体名称  tag。但是我们要在结构体的尾部创建变量,并且在后续程序中不能再创建相同结构体的变量了。

#include<stdio.h>

int main(){
   struct {
      int a;
      double x;
      chua c;
   }x;
   //这里创建一个结构体的时候创建了一个变量x,在后续的程序中不能再创建与x相同类型的结构体变量了。
   return 0;
}

 1.1.4 结构体的自引用

    我们在创建一个结构体变量的时候,能不能将同意类型的结构体当成自己的成员呢?如下:

struct Stu{

      int data;

       struct Stu next; 

};

    如果上述代码可行的话,那么sizeof(struct Stu)的大小多大呢?我们不难发现我们无法计算出其大小,所以我们不能采用这种方式。但是我们可以用以下方式来实现结构体的自引用: 

 我们用存储下一个结构体变量的方式来实现自引用。

struct Stu{
     int data;
     struct Stu *next; 
};


1.1.5 结构体的大小与内存对齐

我们现在已经掌握了结构体的基本使用方式,那么结构体在内存中的大小是多少呢?

那么我们必须要掌握一个十分重要的知识: 结构体的内存对齐

结构体的对齐规则:                                                                                                                                                 1.结构体的第一个变量对齐到结构体变量偏移位置为0的内存处。                                                             2.其他的成员对齐到“对齐数”的整数倍的地方。

  对齐数 = 编译器中默认的对齐数 与 变量大小中的较小值

  ( 在VS中编译器默认的对齐数是8,在Linux的gcc环境下没有默认对齐数。)

3.结构体的大小为最大对齐数(所有变量中最大的对齐数)的整数倍。

4.如果结构体中有结构体的嵌套,嵌套的结构体的最大对齐数是其中最大的对齐数。外层结构体的大小满足规则3.

下面以两个例子来深入体会结构体的对齐规则。

struct SSY{
   char a;
   int b;
   double c;
};//exmple 1.

struct SSS{
   int a;
   char ch[5];
   struct SSY w;
};//exmple 2.

 

 上图为struct SSY在内存之中的存储,类似我们可以得到struct SSS的内存。

那为什么结构体要内存对齐呢?

(1)性能原因

 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

(2)平台原因

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

 总体来说:结构体的内存对齐就是以空间换时间。

1.1.6 结构体传参

我们用一段代码来引入:

#include<stdio.h>

struct SSY{
    char a;
    int b;
    double c;
};

void Print1(struct SSY i){
  printf("%c %d %lf ",i.a ,i.b ,i.c);
}

void Print2(struct SSY *j){
   printf("%c %d %lf " ,j->a ,j->b ,j->c);
}

int main(){
     struct SSY z={'t',4,2.5};
     Print1(z);
     Print2(&z);
     return 0;
}

我们比较一下 Print1 和Print2 函数哪一个更好呢?

答案是:Print2.

我们在进行函数传参的时候要进行压栈的操作,要在栈区拷贝一份临时空间,当我们的结构体过大的时候我们的代码性能就会变差,所以我们在进行结构体传参数的时候进行传地址的操作。

2.2联合体

2.2.1 联合体的声明

联合体像结构体一样,联合体也是由一个或者多个变量构成,但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫:共用体。我们给联合体的其中一个成员赋值时,其他成员的值也跟着变。

#include<stdio.h>

//联合体的声明
union SSY{
  char i;
  int j;
};

int main(){
  union SSY s;//定义一个联合体
  s={1};//给联合体进行赋值
  printf("%zd",sizeof(s));
//计算联合体的大小,结果为:4
  return 0;
}

那我们计算的结果为什么就是4呢?

2.2.2 联合体的内存大小计算

我们通过对联合体和结构体的对比来看看联合体的大小。


struct s{
  char c;
  int i;
};

union u{
  char c;
  int i;
};

联合体的大小至少是最大成员的大小,当最大成员的大小不是最大的对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

#include<stdio.h>

int main(){
  union un{
     char c;
     int i;
};
  union un u={0};
  u.i=0x11223344;
  u.c=0x55;
  printf("%u",u.i);
 //结果为:11223355
  return 0;
}

我们由这个例子可以体会到其中的奥妙,对于此结构体 union un u在内存中的存储如下图:

3.3 枚举

3.3.1 枚举的声明

枚举顾名思义就是一一列举,我们可以定义一个变量将它可能出现的值进行一一列举。

enum DAY{
   Mon,
   Tues,
   Wen,
   Thur,
   Fri,
   Sat,
   Sun,
};

enum SEX{
  male,
  female;
};

这里我们举了两个例子,一个是今天是星期几,第二个是性别。其中enum DAY 和enum SEX是枚举类型,{}中的值是枚举常量。枚举常量在枚举类型中的编号默认是从0开始的,但是我们也可以人为的对其进行修改。

3.3.2 枚举类型的优点

我们从枚举类型的定义可以知道枚举和 # define很相似,其实枚举就是 # define 的plus版本。

那么我们为什么要用枚举呢?

<1>.增加代码的可读性和可维护性。

<2>.和#define定义的标识符比较枚举有类型检查,更加严谨。

<3>.便于调试,在编译器的预处理阶段会删除 # define定义的符号。

<4>.使用方便,一次可以定义多个常量。

<5>.枚举类型遵循作用域的规则。

  • 60
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Main. 24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值