• C复习-结构struct+bit field+union


    参考: 里科《C和指针》


    结构的声明

    struct {
        int a;
        char b;
    } x;
    struct {
        int a;
        char b;
    } y[20], *z;
    
    // 会报错,因为z和x虽然都没有名字,但是并不是一个东西
    z = &x;
    
    struct SIMPLE {
        int a;
        char b;
    };
    // 这样就对了,因为名字匹配
    struct SIMPLE x;
    struct SIMPLE* z = &x;
    
    // 这样写也ok. 此时SIMPLE是类型名,而非结构体标签,因此可以直接用
    typedef struct {
        int a;
        char b;
    } SIMPLE;
    SIMPLE x;
    SIMPLE* z = &x;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    结构体不能自引用(不能包含同结构成员),但可以包含指向同结构的指针。

    struct SELF_REF1 {
    	int a;
    	struct SELF_REF1 b;
    }; // 这个会报错,因为SELF_REF1的定义永远在递归,永远没有完成
    
    struct SELF_REF2 {
    	int a;
    	struct SELF_REF2 *b; 
    }; // 这个是正确的,因为b的长度确定
    
    typedef struct {
    	int a;
    	SELF_REF3 *b;
    } SELF_REF3; // 这个会报错,因为定义b时还没有SELF_REF3
    
    typedef struct SELF_REF3_TAG {
    	int a;
    	struct SELF_REF3_TAG *b;
    } SELF_REF3; // 这样是正确的
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    如果两个结构需要互相依赖(比如一个结构需要包含另一个结构的几个成员),那么至少要有一个结构必须在另一个结构中以指针的形式存在。此时使用不完整声明incomplete declaration。

    // 先不完整声明B
    struct B;
    
    struct A {
    	struct B *partner;
    	...
    };
    
    struct B {
    	struct A *partner;
    	...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    结构的初始化

    类似数组,用嵌套的大括号。如果初始列表的值不够,剩余的会采用缺省值。

    struct INIT_EX {
    	int a;
    	short b[10];
    	Simple c;
    } x = {
    	10,
    	{1, 2, 3},
    	{4, 'x'}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    结构成员的访问

    结构成员的直接访问用 . ,间接访问(通过指向结构的指针访问成员)使用 → 。

    →的优先级高于&和*

    typedef struct {
    	int a;
    	short b[2];
    } Ex2;
    typedef struct EX {
    	int a;
    	char b[3];
    	Ex2 c;
    	struct Ex *d;
    } Ex;
    // 此时指针d是NULL
    Ex x = { 10, "Hi", { 5, {-1, 25} }, 0 };
    Ex *px = &x;
    
    // ->优先级高于&所以不用括号
    int *pi = &px->a;
    int val = px->c.a;
    int val2 = *px->c.b; // -1
    // *px->d 会报错,所以对指针进行解引用之前一定要检查是否有效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    结构存储空间的分配

    编译器按照成员列表的顺序分配空间,如果存储时需要满足边界对齐要求,那么成员之间可能出现空白。比如一个机器int占4字节,起始存储位置必须被4整除,系统禁止编译器在struct的起始位置跳过字节来满足对齐要求,那么,对一个结构:

    struct ALIGN {
    	char a;
    	int b;
    	char c;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其存储结构如下,第一块的第一个字节存a,随后空3个字节,再开始存b,然后进入第三块,第一个字节存c,随后空3个字节。这样可以满足对齐要求,但需要12个字节,空6个。

    □■■■|□□□□|□■■■

    此时如果重新排列,使得int b在第一行,那么前4个字节可以存b,然后再来4个字节存a和c,最后跳过2个,此时只需要8个字节,空2个。

    如果结构数目较少,为了可读性可以将相关的放在一起,但如果有成百上千个结构,为了减少内存浪费,应该考虑重排成员。

    使用sizeof可以看整体占的字节数,offsetof则能看到struct某个元素在第几个字节。

    #include 
    typedef struct {
        int b;
        char a;
        char c;
    } ALIGN;
    
    int main()
    {
        printf("%d\n", sizeof(ALIGN)); // 8
        printf("%d\n", offsetof(ALIGN, c)); // 5 
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    作为函数参数

    直接传struct效率很低,因为需要制造拷贝,复制到堆栈中再丢弃。因此一般是传递指针。(除非结构特别特别小,即长度和指针相同或更小)

    为了进一步提升效率,可以把参数声明为寄存器变量(如果函数对这个指针的间接访问超过3次,会更省时);同时为了避免修改指针,可以声明为const

    // Transaction是一个结构体,这个函数用来打印其中的成员。使用->访问成员
    // 如果函数里需要修改某个成员,就去掉const
    void print(register Transaction const *trans);
    
    • 1
    • 2
    • 3

    位段bit field

    类似struct,但是成员占一个或多个位,最终存储在一个或多个整型变量中。位段的成员只能是int、signed int或unsigned int(最好只用signed和unsigned,因为int被解释为有符号还是无符号是编译器决定的),并且要指明位数。

    如果要保证可移植性,那么不要用位段。

    优点是可以将长度为奇数的数据包装在一起,节省存储空间;可以很方便地访问一个整型值的部分内容,比如在os中与磁盘控制器通信,方便进行位操作。

    struct CHAR {
    	unsigned ch : 7;
    	unsigned font : 6;
    	unsigned size : 19;
    };
    struct CHAR ch1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    联合Union

    union的成员引用的是内存中的相同位置,所以适合不同时刻在同一位置存储不同东西的情况。如果成员们长度不同,union的长度就是最长的成员的长度。

    初始化时,初始值必须是第一个成员的类型,如果不符合,会被转换。

    union {
    	float f;
    	int i;
    } fi = { 1.0f };
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    mysql解压版安装步骤linux
    【暑期每日一题】洛谷 P7540 [COCI2009-2010#1] DOMINO
    Makefile基础
    光谱数据处理:1.特征波长优选的不同方法与Python实现
    [李宏毅老师深度学习视频] 类神经网络训练不起来的四大原因 【手写笔记】
    测试用例的设计方法(全):正交实验设计方法|功能图分析方法|场景设计方发
    useReducer
    SSM框架-SpringMVC(一)
    MATLAB向量化编程基础精讲教程
    Qt 数据库的注册和登录功能
  • 原文地址:https://blog.csdn.net/pxy7896/article/details/134266996