为什么我无法检索灵活的阵列成员大小?

时间:2016-07-28 16:59:54

标签: c arrays language-lawyer sizeof c11

好的,所以我在阅读标准论文(ISO C11)的部分解释了灵活的阵列成员(见6.7.2.1 p18)。它说:

  

作为一种特殊情况,结构的最后一个元素有多个   命名成员可能具有不完整的数组类型;这叫做a   灵活的阵列成员。在大多数情况下,灵活的阵列成员   被忽略了。特别是,结构的大小就像是   柔性阵列成员被省略,除了它可能有更多   拖尾填充比遗漏意味着。但是,当.(或   ->)运算符有一个左操作数,它是一个带有灵活数组成员的结构(指向),右操作数命名该成员,   表现为该成员被最长的数组替换   (具有相同的元素类型)不会使结构更大   而不是被访问的对象;阵列的偏移量应保持不变   柔性阵列成员的那个,即使这与此不同   替换数组。如果这个数组没有元素,那么   表现得好像它有一个元素,但行为是未定义的,如果有的话   尝试访问该元素或生成指针   过去了。

以下是一些例子(p20):

  

示例2声明后:  


     struct s { int n; double d[]; };
   结构struct具有灵活的数组成员d。一种典型的方式  用这个是:  

     int m = /* some value */;

struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));
   并假设对malloc的调用成功,该对象指向  对于大多数用途来说,p表示如同p已被声明为:  

     struct { int n; double d[m]; } *p;
   (在某些情况下,这种等同性被打破;在  特别是,成员d的偏移可能不一样。)

添加了剧透,因为标准中的示例不是文档。

现在我的例子(从标准中扩展一个):

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    struct s { int n; double d[]; };

    int m = 7;

    struct s *p = malloc(sizeof (struct s) + sizeof (double [m])); //create our object

    printf("%zu", sizeof(p->d)); //retrieve the size of the flexible array member

    free(p); //free out object
}

在线example

现在编译器抱怨p->d的类型double[]不完整,根据标准文件显然不是这样。这是GCC编译器中的错误吗?

5 个答案:

答案 0 :(得分:5)

  

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型; ...... C11dr 6.7.2.1 18

以下d是不完整的类型。

struct s { int n; double d[]; };
  

sizeof运算符应应用于具有函数类型或不完整类型的表达式...C11dr§6.5.3.41

// This does not change the type of field `m`.
// It (that is `d`) behaves like a `double d[m]`, but it is still an incomplete type.
struct s *p = foo();

// UB
printf("%zu", sizeof(p->d));

答案 1 :(得分:3)

这看起来像标准中的缺陷。我们可以从论文中看到灵活数组成员的标准化N791 "Solving the struct hack problem",结构定义替换仅适用于评估上下文(借用C ++术语);我的重点:

  

当一个类型为结构的左值时       使用灵活的数组成员用于访问对象,它的行为与       如果那个成员被最长的阵列所取代       结构大于被访问的对象。

比较最终的标准语言:

  

[W]如果.(或->)运算符有一个左操作数,它是(一个指向)具有灵活数组成员的结构,右操作数命名该成员,它的行为好像那个成员被最长的数组所取代(同样的   元素类型)不会使结构大于被访问的对象[...]

某种形式的语言,例如&#34; .(或->)运算符左操作数是(指向)一个结构时一个灵活的数组成员和右操作数名称成员被评估 [...] &#34;似乎可以解决它。

(注意sizeof不会评估它的参数,除了可变长度数组,这是另一个鱼群。)

defect report无法看到相应的JTC1/SC22/WG14 website。您可以考虑通过ISO国家成员机构提交缺陷报告,或要求您的供应商这样做。

答案 2 :(得分:2)

标准说:

C11-§6.5.3.4/ 2

  

sizeof运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或类型的带括号的名称。 大小取决于操作数的类型

它也说 C11-§6.5.3.4/ 1

  

sizeof运算符不适用于具有函数类型或不完整类型的表达式,[...]

p->d类型不完整,不能是sizeof运算符的操作数。声明

它的行为就好像该成员被最长的数组(具有相同的元素类型)替换,这个数组不会使结构大于被访问的对象

不适用于sizeof运算符,因为它通过必须是完整类型的对象类型来确定对象的大小。

答案 3 :(得分:1)

首先,正在发生的事情在标准方面是正确的,声明$(document).ready( function() { $('#dqualification').change(function() { document.getElementById("demo").innerHTML = "Resolve following errors"; }); }); 的数组不完整,您不能使用[]运算符。

但在你的情况下也有一个简单的原因。您从未告诉过您的编译器,在该特定情况下,sizeof成员应被视为特定大小。您只告诉d要保留的总内存大小,并将malloc置于此位置。编译器没有获得可以帮助他推断出数组大小的类型信息。

这与分配可变长度数组(VLA)或指向VLA的指针不同:

p

这里编译器可以知道数组 double (*q)[m] = malloc(sizeof(double[m])); 指向的是什么类型。但不是因为您告诉q总大小(该信息未从malloc调用返回),而是因为mallocm类型规范的一部分。

答案 4 :(得分:0)

当涉及某些语境中某些术语的定义时,C标准有点松散。给出类似的东西:

struct foo {uint32_t x; uint16_t y[]; };
char *p = 1024+(char*)malloc(1024);  // Point to end of region
struct foo *q1 = (struct foo *)(p -= 512); // Allocate some space from it
... some code which uses *q1
struct foo *q2 = (struct foo *)(p -= 512); // Allocate more space from it

没有明确指出对象占用的存储空间 * q1或* q2,也不是q1-> y或q2-> y。如果之后永远不会访问* q1, 然后q2-&gt; y可以被视为uint16_t [509],但是写入* q1将会丢弃 q2-&gt; y [254]及以上的内容,写q2-&gt; y [254]及以上将 垃圾* q1。由于编译器通常无法知道将会是什么 在将来发生* q1时,它无法合理地报告大小 对于q2-&gt; y。