天天看点

C语言中的结构体、联合、枚举结构体联合枚举

结构体

含义:聚合数据类型,能够同时存储超过一个的单独数据。结构体是一些值的集合,这些值称为成员,成员可以是不同的数据类型。

  • 定义结构体
//头部定义
struct TypeName
{
  类型 成员;
    ...;
};

//尾部定义,也叫匿名结构体
struct
{
    类型 成员;
    ....;
} Name = {val1,val2,val3};

//错误示例
//在C++中正确,C中错误
//在定义结构体时,未创建对象,没有给结构体分配内存,因此没有空间赋值。
struct TypeName
{
  	int val1 = 200;
  	char val2 = 'A';
};

//错误示例
//错误原因同上
struct TypeName
{
    static int val1;
    register int val2;
    auto int val3;
};
           
  • 声明结构体变量
  • 初始化
// 成员类型顺序必须正确
struct TypeName 变量名 = {value1,value2,...};

//顺序可改变
struct TypeName 变量名 = {
    .member1 = value1;
    .member2 = value2;
    ...
};

//相同数据类型可直接赋值
struct TypeName val1 = val2;

//结构体指针创建动态对象
struct TypeName *p = (struct TypeName*)malloc(sizeof(struct TypeName));
           
  • 结构体类型重命名

声明结构体变量时,结构体类型前都要加 struct ,较为繁琐,可以给结构体取别名。这样就省去了加struct

//方法一
typedef struct TypeName
{
    ...
}	TypeName;

//方法二
typedef struct
{
    ...
} TypeName;
           

注意点,方法一中,如下两种声明变量是正确的。

//正确
struct TypeName val1;

//正确
TypeName val2;
           

方法二,第一中声明变量是错误的。方法二是匿名结构体,根本就没有TypeName这种结构体类型,因此不能用一种方法声明。

//错误
struct TypeName val1;

//正确
TypeName val2;
           

如果是链表类型,一个节点指向下一个节点,C语言中,下面的示例是错误的。类型名直到声明末尾才定义,在结构体内部,它尚未完成定义。因为在取别名之前,得先有一个名,在取名时,不能用你的别名。

typedef struct TypeName_Node
{
    int val1;
    char val2;
    TypeName* next;
}Typename;
           

解决方案:

typedef struct TypeName_Node
{
    int val1;
    char val2;
    struct TypeName_Node* next;
} TypeName;
           
  • 结构体成员的排列

结构体成员排列的顺序会影响结构体字节数。系统为了快速访问结构的成员,会对结构体的成员内存排列进行对齐和补齐。

C语言中的结构体、联合、枚举结构体联合枚举

32位CPU读取数据时,一次读取4字节,也就是32位,如果一个

int

类型的变量A,存储以0x01开头的地址,那么它所占的内存地址为0x01 - 0x04,也就是说,一部分存储在0x00 - 0x03区间,一部分存储在0x04 - 0x07区间。CPU读取A时,先读取0x00 - 0x03部分, 再读取0x04 - 0x07部分,这样分两次读取。如果直接存储在能整除4的地址,那么只需要读一次。

  1. 对齐:
    假定第一个成员使用0地址,所有成员使用的内存地址必须被它所占的字节数整除(大于4则以4为基准),如果不能则补充空字节。
  2. 补齐:

    结构体的总字节数,必须是它最大成员字节数的整数倍,如果不是,补充空字节

    注意:Linux系统在计算对齐,补齐时,成员的字节超过4则按4计算

struct X
{
    char a;
    int b;
    char c;
}
main()
{
    // 12
    printf("%d\n",sizeof(struct X));
}
           
C语言中的结构体、联合、枚举结构体联合枚举
  1. 第一个成员为

    char

    类型,占一个字节,地址为0x00
  2. 第二个成员为

    int

    类型,由于0x01地址不能整除

    sizeof(int)

    4字节,为了对齐,往后补三个字节,地址为0x04
  3. 第三个成员为

    char

    类型,任何数都能整除1,故地址为0x08
  4. 到此,结构体大小为9字节,最大类型为

    int

    类型,4字节,不是它的倍数,往后补3个字节,共12字节
struct X
{
    int a;
    char b;
    char c;
}
main()
{
    // 8
    printf("%d\n",sizeof(struct X));
}
           
C语言中的结构体、联合、枚举结构体联合枚举
struct X
{
    char a[21];
    int b;
    double c;
};
main()
{
    // 36
    printf("%d\n",sizeof(struct X));
}
           
  1. 第一个成员大小为21个字节,假设地址初始为0,成员1所占地址为0-20
  2. 第二个成员为

    int

    类型,21不能整除4,往后补3个到24,地址24整除4,故成员2所占地址为24-27
  3. 第三个成员为

    double

    类型,8字节大于4字节,以4字节为基准,地址28能整除4,故成员3所占的地址为28-35
struct X
{
    char a[21];
    int b;
    double c;
    short d;
};
main()
{
    // 40
    printf("%d\n",sizeof(struct X));
}
           
  1. 第一个成员大小为21个字节,假设地址初始为0,成员1所占地址为0-20
  2. 第二个成员为

    int

    类型,21不能整除4,往后补3个到24,地址24整除4,故成员2所占地址为24-27
  3. 第三个成员为

    double

    类型,8字节大于4字节,以4字节为基准,地址28能整除4,故成员3所占的地址为28-35
  4. 第四个成员类型为

    short

    类型,2字节,地址36能整除2,成员4所占的地址为36-37
  5. 至此,结构体所占字节数为38个,最大类型大小为

    8

    字节超过4按4来算,38不能整除4,往后补2个字节,共40字节。

联合

含义:是一种由程序设计的一种数据类型,使用语法和结构一样,只是成员的排列方式不同,所有成员共用一块内存,一个成员的值发生改变,其他成员的值,也会跟着变化。联合使一块内存对应多个标识符,达到节约内存的目的。

union TypeName
{
    int val1;
    char val2;
    ...
};
           

联合的成员是天然对齐的,但是有内存补齐。

使用联合判断系统是大端系统还是小端系统。

union X
{
    char ch;
    int num;
}
int main()
{
    union X x;
    x.num = 0x0a0b0c0d;
    if (x.ch == 0x0a)
    {
        printf("小端")
    }
    else
    {
        printf("大端")
    }
}
           

大端系统:低位数据存储在高位地址

小端系统:低位数据存储在低位地址

一般个人计算机使用的是小端系统,服务器、网络设备使用的是大端,大端字节序也叫网络字节序。

枚举

含义:枚举是一种特殊的整形,它是把一个整形数据可能出现的值全部罗列出来,除此之外不应该再使用其他的值,保证安全性。如果第一个枚举未赋值,那么默认为0,如果后面还有成员,那么往后递增1。

//枚举类型定义
enum Direction{Up,Down,Left,Right};

//匿名枚举,只使用枚举值
enum{Up,Down,Left,Right};
           

枚举值是常量,可以直接当常量使用,可使用在

case

语句的后面或者判断语句中,不用再写字面值常量,从而提高程序的可读性。

main(){
    //也可以定义在main函数外
    enum {Up,Down,Left,Right};
    
    switch(getchchar()){
    case Up: break;

    case Down: break;

    case Left: break;

    case Right: break;
    }
}

           

继续阅读