Last Updated: 2023-12-12 04:52:35 Tuesday
-- TOC --
本文总结一下C语言结构体定义时的语法,以及可能会遇到的坑。结构体是一块内存,C语言处理的就是各种大小的内存块!
struct tag{
struct tag A; /* Wrong */
int value;
};
这种声明是错误的,因为这实际上是一个无限循环,成员A是一个结构体,A的内部还会有成员是结构体,依次下去,无限循环。在分配内存的时候,由于无限嵌套,也无法确定这个结构体的长度,所以这种方式是非法的。
这种情况,只能使用指向结构体的指针,下面是正确的定义:
struct tag{
struct tag *A; /* Right */
int value;
};
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;
请参考:字节对齐,写得很详细。
$ 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;
};
看下面这段测试代码:
$ 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内定义成员函数。
#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;
}
int a[8] = {}
,是全部清0,写成{0}
也是全部清0,但是,这是a[0]=0
,然后其它由编译器清零。本文链接:https://cs.pynote.net/sf/c/202111198/
-- EOF --
-- MORE --