enum 枚举类型名 { 枚举符列表, }枚举变量名;
enum 枚举类型名 枚举变量名 = 枚举符;
#include
enum MY_enum {
red,
green = 3,
blue
};
int main() {
enum MY_enum color = red;
printf("%d", color);
return 0;
}
#include
enum {
red,
green,
blue
} color;
int main() {
color = red;
printf("%d", color);
return 0;
}
一些约束
#include
enum MY_enum{
red,
green,
blue
};
int main() {
enum MY_enum color = blue;
printf("%d / %d / %d \n",blue , color, sizeof(color));
// color占4字节跟int一样
int red = 0x05;
color = red;
printf("%d", color);
// 这里color的值变为0x05
enum MY_enum* penum = &color;
printf("%d", *penum);
return 0;
}
#include
struct PointType
{
int x;
int y;
} point1 = {1,2};
int main() {
struct PointType point2 = {.x = 20};
struct PointType* p = &point1;
printf("%d / %d", p->x, p->y);
return 0;
}
{,}为初始化器(initializer),可用.的方式赋值及指定成员的初始化器(designated initializer)
结构体复合字面量(structural compund literal)
对于结构体变量用.的方式引出
对于结构体指针用->的方式引出
- 不能对位域成员做取地址操作(在某些情况下会触发hard fault)
- 位域成员不能用sizeof
- 不能用对齐属性来修饰位域
- 位宽不能超出该类型的大小
#include
#include
enum MYenum {
ENUM1,
ENUM2,
ENUM3
};
int main() {
struct BitField {
uint32_t a : 6;
// uint8_t b : 12; warring:invalid size for bit field
// _Alignas(int32_t) int32_t a : 5; warring:attribute "_Alignas" does not apply to bit fields
uint16_t b : sizeof(ENUM1); // uint16_t b : 4;
enum MYenum e : ENUM3; // enum MYenum e : 2;
}bf1 = {10,20}, bf2 = {.a = 10, .e = ENUM3};
return 0;
}
位域的存储
e y x c b a
(0) 10 1 0000 011101111(00000)01010 011000
高位 <-------------------------------> 低位
#include
#include
#include
int main() {
enum MyEnum {
ENUM1,
ENUM2,
ENUM3
};
struct MyStruct {
int32_t a : 6;
int16_t b : 5;
int8_t c : 8;
char x : 4;
bool y : 1;
enum MyEnum e : 2;
}s = {0x18,0x0a,0x77,'\0',true,ENUM3};
// s 的二进制表示为
// e y x c b a
// (0) 10 1 0000 011101111(00000)01010 011000
// 高位 <-------------------------------> 低位
return 0;
}
如果你使用了符合字面量(compound literal)赋值,gcc编译器会直接将函数内的普通结构体变量升级为global或static一样的变量(即拥有全局作用域或者静态生存期)来赋值
.LC0:
.byte 152
.byte 2
.byte 119
.byte 80
main:
mov eax, DWORD PTR .LC0[rip]
mov DWORD PTR [rbp-8], eax
对于单独对每个结构体变量赋值的操作就会相当麻烦,因为涉及存储时的字节对齐等,需要一堆移位和与或的运算。例如:
movzx eax, BYTE PTR [rbp-8]
and eax, -64
or eax, 24
mov BYTE PTR [rbp-8], al
movzx eax, WORD PTR [rbp-8]
and ax, -1985
or ax, 640
mov WORD PTR [rbp-8], ax
mov BYTE PTR [rbp-6], 119
movzx eax, BYTE PTR [rbp-5]
and eax, -16
mov BYTE PTR [rbp-5], al
movzx eax, BYTE PTR [rbp-5]
or eax, 16
mov BYTE PTR [rbp-5], al
movzx eax, BYTE PTR [rbp-5]
and eax, -97
or eax, 64
mov BYTE PTR [rbp-5], al
;MOVZX — Move With Zero-Extend
;MOVSX/MOVSXD — Move With Sign-Extension
一般会进行4字节对齐,即变量的起始地址一定是4字节的倍数。
_Alignof(表达式)返回表达式所占的字节数
_Alignas()用户自定义该变量的字节对齐方式可以为(0,1,2,4,8,12,4的整数倍)
#include
#include
#include
#include
int main() {
size_t size = _Alignof(max_align_t);
printf("%zu \n", size);
size = alignof(bool);
printf("%zu \n", size);
return 0;
}
- 结构体第一个成员所在的偏移地址为0。
- 每个成员根据其类型或程序员指定对其字节数来判定偏移地址,如果无法堆积,则在两个成员之间补0,直至其地址可以对齐。
- 结构体对象的字节对齐要求与其成员中最大字节对齐要求相一致。
- 结构体对象的大小为其对齐字节数的倍数
#include
#include
#include
#include
#include
int main() {
struct {
int8_t a;
int32_t b;
int16_t c;
int64_t d;
} s = {0x01,0x02,0x03,0x04};
return 0;
/*
a b c d
| | | | | | |
10 00 00 00 20 00 00 00 30 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
*/
}
在#include
这个头文件之后便可使用复数类型。
但是谁会真的这样用捏,毕竟很多运行环境并不支持这么做
#include
#include
#include
#include
int main() {
float complex comp = 2.5f + 1.5f;
comp += 3.0f * I;
float r = crealf(comp);
float i = cimagf(comp);
long double complex comp2 = {1.2f, 2.5f};
return 0;
}
------ By Flier
2024.1.25