【自定义类型】:结构体,位段,枚举,联合

版权声明: https://blog.csdn.net/spaceman_c/article/details/80257580
一. 结构体

1,结构体类型的声明:

struct 结构体名称
{
    类型说明符号 成员名;//成员列表
};

成员表由若干个成员组成,每个成员都是该结构的一个组成部分;
对每个成员也必须作类型说明;
结构体成员可以是标量,数组,指针,甚至是其他结构体;
结构体类型相当与一个模型,声明的时候是不分配内存的,只有定义相应的结构体变量的时候,才会分配实际的内存;

2, 结构体的特殊声明:
        第一类

struct  结构体名
{
    char name[20];
    int  age;      // 成员列表;
}n1, n2;//变量名1,变量名2,变量名n  定义结构体变量

        第二类

struct
{
    char name[20];
    int  age;      // 成员列表;
}n1, n2;//变量名1,变量名2,变量名n  定义结构体变量

        第三类

typedef struct
{
    char name[20];
    int  age;      // 成员列表;
}Node;

3, 结构体的自引用:

typedef struct 
{
    int i;
    int j;
    Node n1;
}Node;

这段代码是错误的!

错误1:直接在结构体中声明另外一个结构体,会出现死循环,如A包括B,B又包括A,A又包括B……使得编译器无法知道结构体的空间大小,因此,无法通过编译;

错误2:typedef还没有将结构体命名为Node,你就在结构体中使用了Node,显然,编译器此时还不知到Node是什么!所以,无法通过编译;

正确的使用方法如下:

typedef struct node
{
   int i,j;
   struct node *n1;
}Node;

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

typedef struct
{
    char name[20];
    int  age;
    char sex[5];
    char tele[15];
}stu;

int main()
{
    stu s = { "张三",20,"男","10086" };//创建变量同时初始化
    printf("%s %d %s %s\n", s.name, s.age, s.sex, s.tele);
    system("pause");
    return 0;
}

5, 结构体内存对齐和计算结构体的大小

为什么存在内存对齐:
        1, 平台原因(移植原因):某些平台只能在特定的地址处访问特定类型的数据;
        2, 性能原因:提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量,但是若从奇地址单元处存放,则需要2个读取周期读取该变量。
        3,结构体的内存对齐是拿空间来换取时间的做法。
结构体内存对齐的规则:
        1,第一个成员在与结构体变量偏移量为0的地址处。
        2,其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,对齐数=编译器默认的一个对齐数与该成员大小的较小值。VS中默认的值为8,Linux中的默认值为8.
        3,结构体总大小为最大对齐数的整数倍
        4,.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
这里写图片描述
二、位段

1, 位段的声明和任何普通的结构体成员相同,但有两个不同:
首先位段成员必须声明为int、signed int或者是unsigned int类型。
在成员名的后面是一个冒号和一个整数,这个整数指定该位段所占用的位的数目。
2,位段的内存分配:
        1.位段的成员可以是int、unsigned int 、signed int、char(属于整型家族)。
        2.位段的空间上是按照4个字节(int)或者1个字节(char)的方式来开辟的。
      3.位段涉及很多不确定因素,位段不跨平台,所以可移植的程序避免使用位段。

 struct A
{
     int a : 2;
     int b : 5;
     int c : 10;
     int d : 30;
};

int main()
{

    printf("%d\n", sizeof(struct A));
    system("pause");
    return 0;
}

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

三、枚举(就是把可能取值一一列举)
1,枚举类型的定义:

enum color
{
    red,
    green,
    blue
};

enum color:       . 是枚举类型
                        . { }中的内容是枚举类型的可能取值,枚举常量。这些可能取值都是有值的,默认从0开始,依次递增,在定义的时候也可以赋初值。
2,枚举的优点:
增加代码的可读性和可维护性;
和#define定义的标识符比较枚举有类型检查,更加严谨;
防止命名污染;
便与调试;
使用方便,一次可以定义多个常量;

四、联合体
        1,联合体类型的定义:联合的声明和结构类似,但它的行为方式却和结构不同,联合的所有成员引用的是内存中的相同位置。 union联合体大小取决于所有成员中,占用空间最大的一个成员的大小。
        判断内存的大小端:

int check_sys()
{
    union Un
    {
        int i;
        char c;
    }un;
    un.i = 1;
    return un.c;//返回1为小端存储,返回0为大端存储
}
int main()
{
    int ret = check_sys();
    if (1 == ret)
    {
        printf("小端存储\n");
    }
    else
    {
        printf("大端存储\n");
    }

    system("pause");
    return 0;
}

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

union U1
{
    char s[11];
    int n;
    double d;
};

        s占11字节,n占4字节,d占8字节,因此其至少需11字节的空间。然而其实际大小并不是11,用运算符sizeof测试其大小为16.这是因为这里存在字节对齐的问题,11既不能被4整除,也不能被8整除。因此补充字节到16,这样就符合所有成员的自身对齐了。从这里可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系,即其大小必须满足两个条件:1)大小足够容纳最宽的成员;2)大小能被其包含的所有基本数据类型的大小所整除;

union U2
{
    char s[5];
    int n;
    double d;
};

        s占5字节,n占4字节,d占8字节,因此其至少需8字节的空间.而char型占用一个字节,int占用四个字节,double占用8字节,其最小公倍数为8字节,所以U2也就是占用8字节就可以了。

猜你喜欢

转载自blog.csdn.net/spaceman_c/article/details/80257580