bit field
位字段以结构体形式声明,结构体为每个字段声明标签,并定义宽度。例如:
struct BitFileds {
unsigned int a :2;
unsigned int b :4;
unsigned int :2;
unsigned int c :8;
};
其中,a、b、c分别占据2、4、8位,中间还有一个匿名字段,占2个位。按常规结构体定义该结构占据4个int大小,实际上它只占一个short大小。
需要注意的是整个struct的长度是“implementaion defined”,也就是说编译器决定。见标准 14882:2003,9.6 节第一款。
移位操作符
【注】移位之后补0,但有唯一例外:右移操作符对 int x=1«31 右移后补位1。
int x = 1<<31; // 1000 0000 0000 0000 0000 0000 0000 0000
x >> 1; // 1100 0000 0000 0000 0000 0000 0000 0000
unsigned int y = 1<<31; // 1000 0000 0000 0000 0000 0000 0000 0000
y >> 1; // 0100 0000 0000 0000 0000 0000 0000 0000
说明:Java的移位操作符和C一样,unsigned类型右移补0;signed类型右移根据符号位:正数补0、负数补1。
位运算技巧:
- 将int型变量a的第k位清0:
a=a&~(1<<k)
- 将int型变量a的第k位置1:
a=a|(1<<k)
定义变长TLV
struct TLV {
uint8_t tag;
uint16_t len;
char value[0];
} __attribute__((packed));
最后的__attribute__((packed))
可以强制不对struct TLV进行字节对齐。
作用域
该loop只执行一遍:
do {
...
continue;
} while(0);
指针
指针加减运算
指针的加减运算是和 指针类型相关 的。如果加减N,则实际指针移动长度为 N*sizeof(指针类型)
,绝对不是单纯的移动数量N。
如果想得到struct内成员之间的偏移量,可以使用 offsetof 宏。 同类型指针之间的差值使用 ptrdiff_t 类型。
声明函数指针
typedef int (*func)(void);
声明了一个指向 int test(void);
函数的函数指针。
声明指向数组的指针
int (*ptr)[10];
声明了一个指向 int num[10];
数组的指针。
注意:
与 int **ptr
的区别,这声明了一个指向指针的指针。
与 int *ptr[10]
的区别,这是声明了一个指针数组。
int (*ptr)[10];
这个定义也可以拆成两句:
typedef int t[10];
t *ptr;
其中,t
代表由10个int组成的数组类型,ptr
则是指向这种类型的指针。
数组
一维数组
int a[10];
int (*pa)[10] = &a;
这里定义了一个数组a
, 一个指向数组的指针 pa
。*pa
表示其指向的数组a
,所以(*pa)[0]
可以获取a[0]
元素。
此外,&a[0]
类型为 int*
,而 &a
类型为 int(*)[10]
,但它们指向的地址相同。
二维数组
int a[5][10];
int (*pa)[10] = &a[0];
pa[0]
和a[0]
地址和类型都相同,指向类型为int[10]
的一维数组。
可以把pa
当作二维数组名来使用,而且pa
比a
更灵活,因为数组名不支持赋值、自增操作,而指针支持。比如,pa++
可以使pa
跳过二维数组的一行,指向a[1]
。
gdb查看数据类型
int a[5] = {1,2,3,4,5};
int* pi = &a+1;
printf("%d\n", *(pi-1));
执行打印 5,可以debug一下a
的数据类型:
(gdb) l
1 int main()
2 {
3 int a[5];
4 }
(gdb) ptype &a
type = int (*)[5]
(gdb)