这次分享几个数组的冷门玩法:柔性数组、负数下标和复合字面量。
一、柔性数组:通信数据包神器
1 2 3 4 5 6
| struct package { int version; int size; char data[0]; };
|
data[0] 不占任何空间,sizeof(struct package) 只有 8 字节(两个 int)。用法是一次性 malloc 出”结构体 + 数据”的连续内存:
1 2 3 4 5
| my_package = malloc(sizeof(struct package) + data_len); my_package->version = 1; my_package->size = data_len; for (int i = 0; i < data_len; i++) my_package->data[i] = 'a' + i;
|
这样包头和数据在一块内存里,一次 free 就能释放,序列化网络包时直接整块发送——网络协议和内核代码里到处都是这种写法。
二、数组下标可以是负数
1 2 3 4
| int array[] = {1, 2, 3, 4, 5}; int *p = &array[-1];
printf("%d\n", p[1]);
|
p[1] 等价于 *(p + 1),而 p 指向 array[-1] 的位置,所以 p[1] 就是 array[0]。下标本质只是指针偏移,正负都合法——只要别越界访问到没分配的内存。
三、复合字面量玩数组
1 2 3
| (int[][1][1][1]){1, 2} (int[]){1} (void* []){a}
|
复合字面量(C99)可以在表达式里直接造数组,配合下标能玩出很多花样,比如 (int* []){a}[0][0] 就是 a[0]。调试指针和数组关系的时候挺好用。
写在最后
柔性数组是这几个里面唯一”正经推荐用于生产”的写法,负下标和复合字面量属于了解即可——知道有这么回事,读别人代码不慌。
完整代码
C语言数组冷知识.c1 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <stdio.h> #include <stdlib.h>
struct package { int version; int size; char data[0]; }; struct package* my_package;
int main() { int data_len = 26;
printf("sizeof(struct package) = %I64d\n", sizeof(struct package)); my_package = malloc(sizeof(struct package) + data_len * sizeof(char));
my_package->version = 1; my_package->size = data_len; for (int i = 0; i <= data_len; i++) { my_package->data[i] = (i == data_len ? '\0' : 'a' + i); }
printf("%s\n", my_package->data);
int array[] = {1, 2, 3, 4, 5}; int *p = &array[-1];
printf("%d\n", p[1]);
return 0; }
|