C语言_自定义类型详解

2023-11-03 09:56:55 浏览数 (2)

前言

提示:这里可以添加本文要记录的大概内容:

一.结构体的声明

1.1结构体的基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量 数组:一组相同类型的集合。


1.2结构的声明


1.3特殊声明

在声明结构的时候,可以不完全的声明。

代码语言:javascript复制
//匿名结构体类型
//匿名结构体类型一般只能用一次
struct
{
    int a;
    char b;
    float c;
}x;
struct
{
    int a;
    char b;
    float c;
}*p ;
int main()
{
    p = &x;  //err
    return 0;
}

上面的两个结构体在声明的时候省略掉了结构体标签(tag) 那么在上面代码的基础上,下面的代码合法吗? p=&x;

警告: 编译器会把上面的两个声明当成完全不同的两个类型。所以是非法的。


1.4结构体的自引用

在结构中包含一个类型为该结构本身的成员是否可以?

代码如下:

代码语言:javascript复制
//struct Node
//{
//    int data;
//    struct Node next;
//};
// 不可行,本质上是错误的 Node next ---  4/8
//                                ---  next
//                                --- 无穷                      
正确的自引用方式
代码语言:javascript复制
struct Node
{
    int data;
    struct Node *next;
};
匿名结构体类型和typedef的结合形式

错误样例:

代码语言:javascript复制
typedef struct 
{
    int data;
    Node *next;
}Node;

解决方案:

代码语言:javascript复制
typedef struct Node
{
    int data;
    struct Node *next;
}Node;

1.5 结构体变量的定义和初始化

结构体定义与初始化
代码语言:javascript复制
struct SN
{
    char c; 
    int i;

}sn1 = { 'q',100 }, sn2 = {.i=200,.c='w'};//全局变量

int main()
{
    printf("%c %dn", sn2.c, sn2.i);
    return 0;
}
结构体里嵌套结构体
代码语言:javascript复制
struct SN
{
    char c; 
    int i;

}sn1 = { 'q',100 }, sn2 = {.i=200,.c='w'};//全局变量

struct S
{
    double d; 
    struct SN sn ;
    int arr[10];

};

int main()
{
    //printf("%c %dn", sn2.c, sn2.i);
    struct S s = { 3.14,{'a',99},{1,2,3} };
    printf("%lf %c %dn", s.d, s.sn.c, s.sn.c, s.sn.i);
    int i = 0;
    for (i = 0; i < 10; i  )
    {
        printf("%d ", s.arr[i]);
    }

    return 0;
}

运行结果:


1.6 结构体内存对齐 – 计算结构体大小

  • offsetof 宏 这个宏可以计算结构体成员相较于结构体其实位置的偏移量 ;头文件 #include<stddef.h>

上面的现象分析,我们发先结构体成员不是按照顺序在内存中连续存放的,而是有一定的对齐规则的。 结构体内存对齐的规则:

  • 1.结构体的第一个成员永远放在相较于结构体变量起始位置的偏移量为0的位置。
  • 2.从第二个成员开始,往后的每个成员都要对齐到某个对齐数的整数倍。

对齐数:结构体成员自身的大小和默认对齐数的较小值。 VS上默认对齐数是8; gcc没有默认对齐数,对齐数就是结构体成员的自身大小。

  • 3.结构体的总大小,必须是最大对齐数的整数倍. 最大对奇数是:所有成员的对齐数中最大的值。
  • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

1.7修改默认对齐数

#pragma 这个预处理指令,这里我们在次使用,可以改变我们的默认对齐数。


1.8 结构体传参

首选print2函数,函数传参的时候,参数需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。

二.位段

2.1 什么是位段

  • 1.位段的成员必须是 int、unsigned int 或 signed int 。
  • 2.位段的成员名后边有一个冒号和一个数字。

2.2 位段的内存分配

  • 1.位段的成员可以是 int unsigned int signed int 或者是char (属于整形家族)类型
  • 2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的。
  • 3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

2.3位段的跨平台问题

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

总结:跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是跨平台的问题存在。


2.4 位段的应用


三.枚举

枚举顾名思义就是一一列举。 把我们的取值一一列举。

3.1枚举类型的定义

枚举,里面是枚举的可能取值,逗号。


3.2 枚举优点

四.联合(共用体)

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

4.1 联合大小的计算

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

0 人点赞