我在一个Android开源代码中遇到了一些我无法理解的语法。有一个看起来像这样的结构:
struct __attribute__((packed)) A
{
uint8_t bla;
uint8_t bla2;
uint8_t someFixedSizeArr[20];
uint8_t padded[0]; //Marks offset to padded data
uint8_t someFixedSizeArr2[30];
uint8_t transformed[0]; //Marks offset to transformed data
int32_t length;
uint8_t result[100];
};
我不明白什么是填充和转换以及它们如何工作。稍后,类型B的ctor(包裹A)不会碰到它们,因此它们在创建B时未初始化,但后来被这样引用:
memmove(&A.padded[padLength], &A.result[A.length], 30);
还有:
someFunc(A.transformed, 30, A.someFixedSizeArr);
那么,从内存角度来看,这些零大小的数组在这里发生了什么?请注意,我读到的是零大小的数组,但这似乎与我阅读的内容不匹配,因为它指出数组必须是结构的最后一个字段,在此情况并非如此。
谢谢
答案 0 :(得分:2)
关于结构中的零尺寸数组,您应该首先了解以下几点:
在所有情况下,零大小的数组声明在ISO C 90中都是违反约束的(这需要诊断):即使作为函数参数,声明的标识符实际上是指针,而不是数组!
C99添加了“灵活数组成员”功能:结构的最后一个元素可以是大小为零的数组:“作为特殊情况,具有多个命名成员的结构的最后一个元素可能具有一种不完整的数组类型;这称为灵活数组成员。
在C90代码中,“ struct hack”是在结构末尾使用大小为[1]的数组实现的。一些编译器在C99之前允许零(例如GNU C)。
我们必须使用offsetof(type,last_member),以便从计算中排除[1]:
struct foo {
/* ... */
int array[ZERO_OR_ONE];
};
/* Correct in C99, if ZERO_OR_ONE is 0
Incorrect in C90, (ZERO_OR_ONE can't be zero). */
size_t foo_plus_3_elems = sizeof (struct foo) + 3 * sizeof(int);
/* Correct in C99 whether or not ZERO_OR_ONE is 0 or 1.
Correct in C90 with ZERO_OR_ONE being 1. */
size_t foo_plus_3_elems = offsetof (struct foo, array) + 3 * sizeof(int);
在这里,“不正确”表示我们计算出的存储量比需要的多一点,通常没有任何缺点。
“在C90中正确”表示实际上正确,而不是明确定义的行为。 “每个人”都在做。
答案 1 :(得分:0)
正如所表达的那样。
uint8_t transformed[0]
实际上是指向具有零个元素的数组的“指针”,其行为类似于a[n]
的任何其他结果,包括您不能访问该数组的[n]
个元素
那么,这有什么用,为什么不只是指向动态大小的数组的指针呢?零长度数组仅在您具有需要动态长度字段的大型结构并且需要在程序,系统,程序运行等之间共享结构时才有用。在这些情况下,您无法在结构内部分配指针,因为它们不会指向接收器系统上的内存。有效地,该结构是可变大小的。
请注意length
下方名为transformed
的字段;这很可能是transformed
的数组长度。为使接收方计算结构的大小,接收器将读取长度并计算已转换的大小以确定结构的大小。 (length*sizeof(uint8_t)
)
您可以将零长度数组看作是指针,其内容本身是内联的,非常类似于灵活数组成员,但是它缺乏灵活数组成员的安全性(如果可以称之为安全性)。
您没有提供足够的代码来探究uint8_t padding[0]
可能在做什么,但是与__attribute__((packed))
结合使用会使我相信他们认为必须手动管理该结构的内存布局,但是再次,因此缺少相关代码来推断出来。