C语言进阶学习日志:自定义类型 (细中细)

 

目录

结构体:

结构体的内存对齐:

 为什么存在内存对齐?

位段:

位段的内存分配:

位段的跨平台问题:

枚举类型:

联合体:

 联合体大小的计算:


对于类型有这样的分法:

内置类型:

  1. char
  2. short
  3. int
  4. long
  5. long long
  6. float
  7. double

自定义类型:

  1. 结构体
  2. 枚举
  3. 联合

结构体:

当我们描述一个对象 但发现他有许多属性时 这时候这些属性就是结构体的成员变量 这些成员变量可以时不同的类型 也就时有着不同的属性

    经典的结构体是这样的

    struct stu //stu 是标签 因为有时我们需要用到多个结构体 那么标签就是为了区别他们
    {                  如果不要标签 那他就是匿名结构体 后面会说到
        memberlist 成员列表
    }variablelist 变量列表
     s1 s2 s3

如果你对把一个结构体赋值给另一个结构体 即使两个结构体成员变量相同 但他们时匿名结构体的话 那么是不可以的 因为编译器会认为你左右的两个类型时不同的

    定义的方法:
    struct stu
   {
      ~~~~~~
   }s1={~~~~};

   int main()
  { 
    struct stu s2={~~~~~~};
  }

结构体的内存对齐:

在了解结构体的内存对齐之前首先我们要知道一个 宏 offsetoff

这个可以返回一个结构体成员在结构体里面偏移量的大小 记住他的头文集 <stddef.h>

    #include <stdio.h>
    #include <stddef.h>
    
    struct stu
    {
       char i;
       int c;
    }s1;

    int main()
    {
       printf("%d",offsetof(struct stu,i)); //0
       printf("%d",offsetof(struct stu,c)); //4
    }

 我们再定义一个结构体来解释这个现象

我们先看看内存对齐的规则

①第一个成员在结构体偏移量为0的地址处

②其他成员变量(从第二个变量开始)要对齐到某个数字(对齐数)的整数倍的地址处

对齐数是编译器默认的一个对齐数 与 该成员大小的 最小值

     vs默认对齐数是 8

③结构体的总大小为最大对齐数的整数倍

④如果嵌套了结构体的情况 嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体里面的对齐数)的整数倍

(linux没有默认对齐数 对齐数就是其自身的大小)

 为什么存在内存对齐?

平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能再某些地址处去某些特定的类型数据 

性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐

其实总的来讲内存对齐 就是 空间换时间的做法 

同样默认对其数是可以修改的  使用  #pragma pack(8) 就可以修改默认对齐数为8

使用  #pragma pack()还原默认对齐数

其次对于结构体传参 还是传地址的要好

位段:

位段的声明和结构是类似的 但还是有不同的

位段的成员必须是int,unsigned int,signed int 但这只是规定 char也没什么事

位段的成员名后边有一个冒号和数字

位段的内存分配:

  1. 位段的空间是按照需要以4个字节或者1个字节的方式来开辟的
  2. 位段涉及很多不确定的因素,位段是不跨平台的,注重可执行程序应该避免使用位段

 位段很大的作用就是可以帮助我们优化 节省空间  有些变量只要那么多的bit位 我们就给他那么多 这样就可以很好的节省空间

#include <stdio.h>
struct stu
{
  char a:3;
  char b:4;
  char c:5;
  char d:4;
}s;

int main()
{
  s.a=10;
}

这种给有位段的结构体成员变量赋值时  也要注意位段的限制  

好像这里给a赋值10 他的二进制码 1010 但是在赋值时 我们分配给a的是三个bit位 那么存放时就只能放三个bit位的值  也就是是说只能放  010  进去

对于连续存放时 从地位往高位 放入

位段的跨平台问题:

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

②位段中的最大数目不能确定(早期有些机器是是16位机器 最大数目是16 32位机器的最大数目是32)所以如果一个机器是16位的位段设置为 大于它的 就会出问题

③ 位段中的成员在内存中是从左向右分配 还是从右向左分配 这个也是没有定义的

④ 当一个结构体包括两个位段,如果第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃还是利用,这是不确定 (这就跟我们说的一样)

总的来讲 位段是不支持跨平台的

枚举类型:

枚举顾名思义 就是 列举  把一些数据列举出来 

enum u
{
  //枚举的可能取值
  EXIT,0 这只是初始的
  MON, 1
  THE, 2
  WED  3


};

enum s
{
 DAY=7,
 ONE=6,  这时候FOR为7
 FOR
};

int main()
{
  enum u d = EXIT;
  enum u d = 0;//这样赋值是不建议的 在c语言里面它可能没有报错 但在c++里面检查更严格后就会报错了
               //因为c++会认为 d是枚举类型 0是int类型 不会让他们这样赋值
}

注意 枚举成员都是常量

枚举看起来虽然功能单一 但它也是有优点的

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型 也就是说枚举有类型检查,更加严谨
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可定义多个常量

联合体:

union u
{
  char c;
  double i;
};

int main()
{
  printf("%d\n",sizeof(u));  8  最大成员的大小
  printf("%p\n",&u);
  printf("%p\n",&(u.c));
  printf("%p\n",&(u.i));
  //后面的地址是一样的
}

可以发现 c 和 i 的其实地址是一样的 使用的第一个字节重复了 所以联合体还有个名字叫共用体  当然共用体也有个缺点 就是同一时间只能使用一个变量 每次使用一个变量 另一个变量也会跟着改变所以 每次只能使用一个变量

所以联合体就有了一个应用场景 就是当你每次使用时 都会用到不同的东西 但每次只使用一个的时候 就可以用了

知道了这种特性后 我们就可以用这个特性来判断我们的机器是大端还是小端

    int Checksys()
   {
     union u
     {
        char i;
        int c;
     }s;
     s.c=1;
     return s.i;
   }

 联合体大小的计算:

联合的大小至少是最大成员的大小。

当最大成员的大小不是最大对齐数的整数倍时,就要对齐最大对齐数的整数倍

大家来计算一下这个联合体的大小是多少

union un
{
  short  c[7];
  int  i;
};

 

union un
{
  short  c[7]; 2-8-2 
  int  i;      4-8-4
};
  最终最大对齐数是4 也就是要4的倍数 其中最大成员大小是14 那么最终的联合体大小是16

那么到这里 我们自定义类型的讲解就没有了 感谢大家能看到这里 希望大家都能收到自己心仪的offer!!!!


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值