自定义类型 4个月前

编程语言
276
自定义类型

一、结构体

1.匿名结构体类型

我们来举个例子介绍匿名结构体

struct  //创建时只有关键字,即为匿名结构体
{
  char a;
  int b;
  char c;
  double d;
}s;

再来列举一个匿名结构体常见的错误

struct  
{
  char a;
  int b;
  char c;
  double d;
}s;        

struct  
{
  char a;
  int b;
  char c;
  double d;
}*p;        //用p指向这个匿名结构体

int main()
{
  p = &s;  //观察两个匿名结构体发现组成成分一样,是不是就可以通过这种方式使 p 指针存储 s 呢?
           //error  虽然类型一样,但是在编译器看来这是两个不同的匿名结构体,非法
  return 0;
}

2.结构体的自引用

举个例子来看看结构体如何相互引用

struct A
{
  int i;
  char c;
};

struct B
{
  char c;
  struct A sa;  //结构体类型 struct B 中含有结构体类型 struct A;
  double b;
};

那么结构体自引用是否为以下这种方式呢

struct N
{
  int d;
  struct N n; //error 无限套娃 ,如果计算大小就直接爆炸
};

正确方法如下

struct N
{
  int d;
  struct N* next; //通过指针找到同类型的下一节点
};

3.结构体的大小计算

结构体的对齐规则

1.第一个成员在与结构体偏移量为0的地址处 2.其他成员变量要对齐到(对齐数)的整数倍的地址处 . 对齐数 = 编译器默认的对齐数 与 该成员大小的较小值 VS默认为8(并且该默认值可以通过编码修改) . 3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍 4.如果该结构体嵌套了另一个结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处 结构体整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

我们以一张图来整明白这些话

image

前面提到过可以修改默认对齐数

以下

#pragma pack()  //括号内的数字即是你要的默认对齐数
/
/
//使用完结构体后再恢复默认对齐数为8=
#pragma pack()  //括号内不放数字

举个例子

#pragma pack(2)
struct A
{
  char a;
  int b;
  char c;
};
#pragma pack()

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

二、位段

1.位段的介绍

位段的声明和结构体是类似的,但有两个不同 1.位段的成员必须是int、unsigned int 或 signed int 2.位段的成员后边有一个冒号和一个数字

例如

struct A
{
  int a:2;  //a成员占2bit位
  int b:5;  //b成员占5bit位
  int c:10; //c成员占10bit位
  int d:30; //d成员占30bit位
}           //此时A就是一个位段类型

为什么好好的一个整形,偏要把它硬塞进2bit呢? 节省空间!!

比如说表示性别,2个字节就足矣

00 保密
01
11

2.位段的大小

还是以上述代码举例子

//2+5+10+30=47
//识别出int,创建一个int的大小,即32bit
struct A
{
  //创建4字节----32bit
  int a:2;  //a成员占2bit位
  int b:5;  //b成员占5bit位
  int c:10; //c成员占10bit位
  
  //还留下15bit
  //d塞不进去了 再创建4字节 ---32bit
  int d:30; //d成员占30bit位
}           

int main()
{
  printf("%d\n",sizeof(struct A)); //结果为4+4 = 8字节
  return 0;
}

但其实,在不同的环境下结果可能是不同的

比如在上述代码中,d塞不进去后,是继续用上一次留下的15bit,还是直接塞进新创建的呢?

所以说

位段涉及到很多不确定因素,位段是不跨平台的 注重可移植的程序应该避免位段

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;

image

image

4.位段跨平台问题

1.int位段被当成有符号数还是无符号数是不确定的 2.位段中的最大位的数目不能确定(16位机器最大16,32位最大32,若写成大于16的数再16位机器就会出问题) 3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义 4.当一个结构体包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时 —是舍弃剩余的位还是利用,这是不确定的

三、联合(共用体)

1.联合类型的定义

联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员 特征是这些成员共用一块空间(所以联合也叫共用体) . 所以联合体的大小最小是最大成员的大小

2.利用联合特征判断机器大小端字节序

int check_sys()
{
  union U
  {
    char c;
    int i;
  }u;
  u.i = 1;  // 00 00 00 01
            // 01 00 00 00
  return u.c;
  //返回1是小端
  //返回0是大端
}

3.联合大小的计算

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

比如

union Un
{
  char arr[5]; //对齐数为1   
  int b;       //对齐数为4
               //又因为至少为5(arr为最大成员)
               //所以对齐至8
};
image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2223320
累计阅读

热门教程文档

PHP
52小节
Vue
25小节
MyBatis
19小节
Kotlin
68小节
Swift
54小节