结构体struct的定义和初始化

Last Updated: 2023-12-12 04:52:35 Tuesday

-- TOC --

本文总结一下C语言结构体定义时的语法,以及可能会遇到的坑。结构体是一块内存,C语言处理的就是各种大小的内存块!

错误01:循环结构体

struct tag{
    struct tag A;  /* Wrong */
    int value;
};

这种声明是错误的,因为这实际上是一个无限循环,成员A是一个结构体,A的内部还会有成员是结构体,依次下去,无限循环。在分配内存的时候,由于无限嵌套,也无法确定这个结构体的长度,所以这种方式是非法的。

这种情况,只能使用指向结构体的指针,下面是正确的定义:

struct tag{
    struct tag *A;   /* Right */
    int value;
};

错误02:undefined type

typedef struct tag{
    Node *A;   /* Wrong:Node is undefined here */
    int value;
} Node;

以上定义,使用了指针,但是还是错的,因为typedef定义的类型名称Node,在编译器解析这个结构体内部时,Node还属于未定义。

正确的定义方式如下:

typedef struct tag{
    struct tag *A;   /* Right by using original name */
    int value;
} Node;

关于结构体的字节对齐

请参考:字节对齐,写得很详细。

bit-field位域

$ cat test.c
#include <stdio.h>

struct bits{
    unsigned char a:1;
    unsigned char b:1;
} bits;

int main(void) {
    bits.a = 1;
    bits.b = 0;
    printf("bits.a = %d\n", bits.a);
    if (bits.a == 1) printf("yes!!\n");
    printf("bits.b = %d\n", bits.b);
    printf("sizeof(bits) = %zu\n", sizeof(bits));
    return 0;
}
$ ./test
bits.a = 1
yes!!
bits.b = 0
sizeof(bits) = 1

貌似定义的时候,必须要有unsigned

上面那个结构体,还可以用下面的语法定义,效果一样!

struct bits{
    unsigned char a:1,  # comma here
                  b:1;
} bits;

注意bit-field结构体的sizeof!

无名位域

看到网上有文字说,可以存在无名位域,用来占位,目的是不让一个位域跨字节。这肯定不对!位域必然有可能跨字节。但无名位域语法上是存在的,无名位域不能被代码引用,也就只能用来占位,也许某些情况下,可以提速,比如两个连续5bit的位域,如果中间有3个bit被占位,这两个5bit的位域就分布在两个byte内,而不是第2个位域跨2个byte,后者时,访问第2个位域时,显然会慢一点点:

struct bf {
    unsign char a:5,
                 :3,
                b:5;
};

没有typedef,或没有name

看下面这段测试代码:

$ cat test.c
#include <stdio.h>

typedef struct aa{
    char a;
    char b;
} aa;

struct bb{
    char a;
    char b;
    short c;
} bb;

int main(void) {
    printf("sizeof(aa):%zu\n", sizeof(aa));
    printf("sizeof(bb):%zu\n", sizeof(bb));
    return 0;
}
$ gcc test.c -o test
$ ./test
sizeof(aa):2
sizeof(bb):4

不用typedef,bb其实是一个结构体变量!

如果是这样的定义,叫做无名结构体

struct {
...
} var;

这个var是一个变量,不是type,而且,代码中跟var一样的struct只有这一个,因为我们没有定义这个struct name!

下面这种定义,用变量名覆盖了结构体的name,实际效果跟上面一样,代码中只有变量名,没有结构体的name:

struct nov {
...
} nov;

结构体嵌套

这种定义应该很自然,不存在理解上的问题:

$ cat test2.c
#include <stdio.h>

struct {
    int a;
    int b;
    struct {
        int c;
        int d;
    } ins;
} sis;

int main(void){
    sis.a = 1;
    sis.b = 2;
    sis.ins.c = 3;
    sis.ins.d = 4;
    printf("sis.ins.c = %d\n", sis.ins.c);
    printf("sizeof(sis) = %zu\n", sizeof(sis));
    return 0;
}
$ ./test2
sis.ins.c = 3
sizeof(sis) = 16

结构体变量初始化

struct结构体变量的初始化,请看如下代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>


struct ab {
    int a;
    char b;
} ab1={1,'a'},ab2={2,'b'};

/* tagged structure initialization */
struct ab ab3 = {
    .a = 3,
    .b = 'c'  // last comma can be omitted
};

struct ab ab7 = {
    .a = 7,
};

struct ab ab4 = {4, 'd'};

strict ab ab8 = {}; // all zero!


int main(void) {
    struct ab ab5, *ab6;
    ab5.a = 5;
    ab5.b = 'e';
    ab6 = (struct ab *)malloc(sizeof(struct ab));
    assert(ab6 != NULL);
    ab6->a = 6;
    ab6->b = 'f';

    printf("ab1.a=%d, ab1.b=%c\n", ab1.a, ab1.b);
    printf("ab2.a=%d, ab2.b=%c\n", ab2.a, ab2.b);
    printf("ab3.a=%d, ab3.b=%c\n", ab3.a, ab3.b);
    printf("ab4.a=%d, ab4.b=%c\n", ab4.a, ab4.b);
    printf("ab5.a=%d, ab5.b=%c\n", ab5.a, ab5.b);
    printf("ab6.a=%d, ab6.b=%c\n", ab6->a, ab6->b);
    printf("ab7.a=%d, ab7.b=%s\n", ab7.a, (ab7.b=='\x00'?"\\x00":"what?"));
    free(ab6);
    return 0;
}

注意tagged structure initialization的初始化方式,没有初始化的成员全部是0!

ab8的初始化语法,是C++推荐的...

注意没有显式初始化的成员,编译器会默认将它们设置为0!

C++中,结构体定义可以省略typedef关键词,这种传统的C结构体,在C++中还有一个名称,叫做POD,Plain Old Data class。是的,在C++中,struct也是一种user-defined class,可以在struct内定义成员函数。

C++对struct的扩展

结构体变量之间赋值

#include <stdio.h>
#include <stdbool.h>


typedef struct xyz{
    int a;
    bool b;
    char c;
} xyz;

int main(void) {
    xyz x = {1,0,'x'};
    xyz y = {};
    y = x;  // just like copy assignment in C++
    printf("%d %d %c\n", y.a, y.b, y.c);
    return 0;
}

这种赋值,对于嵌套结构体,一样有效:

#include <stdio.h>
#include <stdbool.h>


typedef struct xyz{
    int a;
    bool b;
    char c;
} xyz;


typedef struct you{
    int a;
    xyz x;
}you;


int main(void) {
    xyz x = {1,0,'x'};
    you y = {2,x};
    you z = y;  // just like copy constructor in C++
    printf("%d %d %d %c\n", y.a, y.x.a, y.x.b, y.x.c);
    printf("%d %d %d %c\n", z.a, z.x.a, z.x.b, z.x.c);
    return 0;
}

本文链接:https://cs.pynote.net/sf/c/202111198/

-- EOF --

-- MORE --