自定义类型详解 2

简介: 自定义类型详解

3.2练习

#include<stdio.h>
struct abc
{
    char a;
  int b;
  char c;
};
int main()
{
  printf("%d", sizeof(struct abc));
}

第一步,将char a放进首地址即偏移量为0所在的空间,占了1,下一个空间的偏移量为1,因int型的对齐数为4,故对齐到偏移量为4时存储,存放4个字节,4,5,6,7的空间被占据,故最后char c会存放在偏移量为8的位置,空间大小好像是9个字节,但别忽略了第三条,结构体总大小为最大对齐数的整数倍,这里的最大对齐数,显然是4,故结构体大小应为12

最后来一道复杂点的

#include<stdio.h>
struct abc
{
    char a;
  int b;
  char c;
};
struct xyz
{
  char x;
  float y;
  struct abc xyz;
};
int main()
{
  printf("%d", sizeof(struct xyz));
}

首先将char x放进偏移量为0所在的空间,y的对齐数为4,故从偏移量为4的位置存储,偏移量为4,5,6,7的空间被float y使用,接着是结构体abc类型的存储,根据第四条,结构体在存储之前也得先对齐,观察abc,发现abc的最大对齐数为4,刚好我们此时的位置就是偏移量为8的位置,是4的倍数,故直接存储即可。先将char a存放进去,偏移量为8的空间被占据,再存放int b,此时,到了偏移量为9的空间,先对齐到偏移量为4的倍数的位置,即12,故12,13,14,15的空间被int b占据,偏移量为16的位置被char c占据,结构体xyz的空间似乎已经确实,为17,但根据第四条规则,结构体的大小,为所有最 大对齐数的整数倍,即4的倍数,为20

85ce5060246046809cd6b5db53112b37.png

4.拓展:结构体实现的位段操作

4.1什么是位段

位段的声明和结构是类似的,有两个不同:

1.位段的成员必须是 int、unsigned int 或signed int(其实char也可以,因为字符型在内存中的存储使用的是ASCII码值的形式,可以这样理解,一个一个的字符,为一个一个的数)

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

#include<stdio.h>
struct abc
{
  int a : 3;
//冒号后面的数字代表的二进制位的数量,这里可看作用3个二进制位存储整型a
  int b : 20;
  int c : 20;
};
int main()
{
  printf("%d", sizeof(struct abc));
}

试想一下,这里打印出来的结果是多少,存储int a我们用了3个二进制位,存储int b用了20个二进制位,存储int c也用了20个二进制位,那么一共用了43个二进制位(bit位),而一个字节为8个bit位,那么是不是就用了6个字节来存储这个结构体呢?

253e92c2af154a00b963b44e4a385ac6.png 在vs上,a用了3个二进制存储,b用了20个二进制位存储,此时还没达到32bit位,但是当把c用20个二进制位存储的时候就会发现,超出一个整型的大小了,vs采用的方式便是再开辟一个整型的空间给你存放这个c,之前剩下的就不要了。但由于c语言对于位段粗糙的定义,导致在不同的编译器有不同的实现,有的编译器秉承着不浪费的原则,先用完之前剩下的空间再开辟,所以位段的使用尽量不要跨平台。要注意的一点:先位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟。在vs上只有char时按1个字节开辟,当既有char又有int或者只有int的时候按4个字节开辟。


4.2位段有什么用?

聪明的小伙伴恐怕早就想到了,3这个数字,我用2个bit位就能够存放,但是我创建了一个整型,我就用了32个bit位去存放,因此,位段可以很好的节省空间。


4.3.位段的跨平台问题

(1)int 位段被当成有符号数还是无符号数是不确定的。

(2) 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机

器会出问题。

(3) 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

(4) 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是

舍弃剩余的位还是利用,这是不确定的。

二、枚举类型

1.枚举类型的定义

#include<stdio.h>
enum sex
{
  male,
  female,
  no
};//这里就定义了一个关于性别的枚举类型,它会按照从0开始的顺序给male,female,no赋值
int main()
{
  int arr1[no] = {9,7};
  //值得再次强调的一点,它定义出来的是常量(而非常变量),可以用在数组中
  printf("%d %d %d", male, female, no);
}

当然你也可以自己给它们赋值,不用根据默认的来

#include<stdio.h>
enum sex
{
  male=3,
  female,
  no=99
};//这里就定义了一个关于性别的枚举类型,它会按照从0开始的顺序给male,female,no赋值
int main()
{
  int arr1[no] = {9,7};
  //值得再次强调的一点,它定义出来的是常量(而非常变量),可以用在数组中
  printf("%d %d %d", male, female, no);
}

2.枚举的优点

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

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

3. 防止了命名污染(封装)

4. 便于调试

5. 使用方便,一次可以定义多个常量

三、联合体类型

1.联合体类型的定义

这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。

union abc
{
  int a;
  int b;
  char c;
};
int main()
{
  union abc x;
  x.a = 20;
  x.b = 10;
  x.c = 'a';
  printf("%d %d %c\n",x.a,x.b,x.c);
  printf("%d\n", x.a);
  printf("%d\n", x.b);
}

由此可知,改变联合体上的一个数就会导致牵一发而动全身的效果

2.联合体类型的特点

(1)牵一发而动全身

(2)所有的数据都从同一个地址开始存储

union abc
{
  int a;
  int b;
  char c;
};
int main()
{
  union abc x;
  x.a = 20;
  x.b = 10;
  x.c = 'a';
  printf("%p %p %p\n", &(x.a),&(x.b),&(x.c));
}

今天的分享到这里就结束了,感谢各位友友的来访,祝各位友友前程似锦O(∩_∩)O

相关文章
|
1月前
|
存储 Linux C++
自定义类型讲解
自定义类型讲解
67 0
|
8天前
|
存储 编译器 Linux
自定义类型详解(1)
自定义类型详解(1)
22 5
|
8天前
自定义类型详解(2)
自定义类型详解(2)
23 1
|
1月前
|
存储 移动开发 API
其他内置类型
本文介绍了 .NET 中的 Console 类和 Environment 类。Console 类提供了控制台输入输出的功能,如设置背景色和前景色、打印文本、读取行和发出蜂鸣声。而 Environment 类则包含有关全局环境的信息和方法,如当前目录、进程路径、处理器数量、操作系统信息等。另外,文章还提及了 .NET Framework 的 AppDomain(用于表示应用程序域,但在 .NET Core 中功能减弱)和 .NET Core 中新引入的 AppContext 类,用于存储全局数据和开关。
|
1月前
|
编译器 Linux C++
自定义类型详解
自定义类型详解
|
1月前
|
编译器 C++
自定义类型
自定义类型
|
1月前
|
C++
c++基本内置类型
c++基本内置类型
34 0
|
6月前
|
存储 算法 程序员
自定义类型总结
自定义类型总结
50 0
|
7月前
|
编译器 C++
自定义类型超详细解答!!!!!(上)
自定义类型超详细解答!!!!!
|
7月前
|
存储
自定义类型超详细解答!!!!!(下)
自定义类型超详细解答!!!!!