简体   繁体   English

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

[英]Why can't I retrieve my flexible array member size?

OK so I was reading the standard paper (ISO C11) in the part where it explains flexible array members (at 6.7.2.1 p18). 好的,所以我正在阅读标准论文(ISO C11)的部分,它解释了灵活的阵列成员(见6.7.2.1 p18)。 It says this: 它说:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; 作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型; this is called a flexible array member. 这被称为灵活的阵列成员。 In most situations, the flexible array member is ignored. 在大多数情况下,将忽略灵活数组成员。 In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. 特别地,结构的尺寸好像省略了柔性阵列构件,除了它可以具有比省略意味着更多的拖尾填充。 However, when a . 但是,当一个. (or -> ) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed ; (或-> )运算符有一个左操作数,它是一个带有灵活数组成员的结构(一个指针),右操作数命名该成员, 它的行为就好像该成员被最长的数组替换(具有相同的元素类型) )不会使结构大于被访问的对象 ; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. 数组的偏移量应保持为灵活数组成员的偏移量,即使这与替换数组的偏移量不同。 If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it. 如果此数组没有元素,则其行为就好像它有一个元素,但如果任何尝试访问该元素或生成一个超过它的指针,则行为是未定义的。

And here are some of the examples given below (p20): 以下是一些例子(p20):

EXAMPLE 2 After the declaration: 例2声明后:

 struct s { int n; double d[]; }; 
the structure struct s has a flexible array member d. 结构struct具有灵活的数组成员d。 A typical way to use this is: 使用它的典型方法是:
 int m = /* some value */; 
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));
and assuming that the call to malloc succeeds, the object pointed to by p behaves, for most purposes, as if p had been declared as: 并且假设对malloc的调用成功,p指向的对象在大多数情况下都表现得像p被声明为:
 struct { int n; double d[m]; } *p; 
(there are circumstances in which this equivalence is broken; in particular, the offsets of member d might not be the same). (在某些情况下,这种等价性被破坏;特别是,成员d的偏移量可能不同)。

Added spoilers as examples inside the standard are not documentation. 添加剧透作为标准内部的示例不是文档。

And now my example (extending the one from the standard): 现在我的例子(从标准中扩展一个):

#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
}

Online example . 在线示例

Now the compiler is complaining that p->d has incomplete type double[] which is clearly not the case according the standard paper. 现在编译器抱怨p->d有不完整类型double[] ,这显然不是标准文件的情况。 Is this a bug in the GCC compiler? 这是GCC编译器中的错误吗?

As a special case, the last element of a structure with more than one named member may have an incomplete array type ; 作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型 ; ... C11dr 6.7.2.1 18 ...... C11dr 6.7.2.1 18

In the following d is an incomplete type. 在下面的d是不完整的类型。

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

The sizeof operator shall not be applied to an expression that has function type or an incomplete type ... C11dr §6.5.3.4 1 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));

This looks like a defect in the Standard. 这看起来像标准中的缺陷。 We can see from the paper where flexible array members were standardized, N791 "Solving the struct hack problem" , that the struct definition replacement is intended to apply only in evaluated context (to borrow the C++ terminology); 我们可以从论文中看到灵活的数组成员是标准化的, N791“解决结构黑客问题” ,结构定义替换仅适用于评估的上下文(借用C ++术语); my emphasis: 我的重点:

When an lvalue whose type is a structure with a flexible array member is used to access an object , it behaves as if that member were replaced by the longest array that would not make the structure larger than the object being accessed. 当类型是具有灵活数组成员的结构的左值用于访问对象时 ,其行为就好像该成员被最长的数组替换,该数组不会使结构大于被访问的对象。

Compare the eventual standard language: 比较最终的标准语言:

[W]hen a . [W]母鸡. (or -> ) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed [...] (或-> )运算符有一个左操作数,它是一个带有灵活数组成员的结构(一个指针),右操作数命名该成员,它的行为就好像该成员被最长的数组替换(具有相同的元素类型) )不会使结构大于被访问的对象[...]

Some form of language like " When a . (or -> ) operator whose left operand is (a pointer to) a structure with a flexible array member and whose right operand names that member is evaluated [...] " would seem to work to fix it. 某种形式的语言,如“ 当一个. (或-> )运算符, 左操作数是(一个指针)一个具有灵活数组成员的结构, 右操作数命名该成员被评估 [...] ”似乎工作要解决这个问题。

(Note that sizeof does not evaluate its argument, except for variable length arrays, which are another kettle of fish.) (请注意, sizeof不会评估其参数,除了可变长度数组,这是另一个鱼的水壶。)

There is no corresponding defect report visible via the JTC1/SC22/WG14 website . 通过JTC1 / SC22 / WG14网站没有相应的缺陷报告 You might consider submitting a defect report via your ISO national member body, or asking your vendor to do so. 您可以考虑通过ISO国家成员机构提交缺陷报告,或要求您的供应商这样做。

Standard says: 标准说:

C11-§6.5.3.4/2 C11-§6.5.3.4/ 2

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. sizeof运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或类型的带括号的名称。 The size is determined from the type of the operand . 大小由操作数的类型确定

and it also says 它也说
C11-§6.5.3.4/1 C11-§6.5.3.4/ 1

The sizeof operator shall not be applied to an expression that has function type or an incomplete type , [...] sizeof运算符不应用于具有函数类型或不完整类型的表达式,[...]

p->d is of incomplete type and it can't be an operand of sizeof operator. p->d是不完整的类型,它不能是sizeof运算符的操作数。 The statement 该声明

it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed 它的行为好像该成员被最长的数组(具有相同的元素类型)替换,这个数组不会使结构大于被访问的对象

doesn't hold for sizeof operator as it determine size of the object by the type of object which must be a complete type. 不适用于sizeof运算符,因为它通过必须是完整类型的对象类型来确定对象的大小。

First, what is happening is correct in terms of the standard, arrays that are declared [] are incomplete and you can't use the sizeof operator. 首先,正在发生的事情在标准方面是正确的,声明[]数组是不完整的,你不能使用sizeof运算符。

But there is also a simple reason for it in your case. 但在你的情况下也有一个简单的原因。 You never told your compiler that in that particular case the d member should be viewed as of a particular size. 您从未告诉过您的编译器,在该特定情况下,应将d成员视为特定大小。 You only told malloc the total memory size to be reserved and placed p to point to that. 您只告诉malloc要保留的总内存大小,并将p放置为指向该内存大小。 The compiler has obtained no type information that could help him deduce the size of the array. 编译器没有获得可以帮助他推断出数组大小的类型信息。

This is different from allocating a variable length array (VLA) or a pointer to VLA: 这与分配可变长度数组(VLA)或指向VLA的指针不同:

 double (*q)[m] = malloc(sizeof(double[m]));

Here the compiler can know what type of array q is pointing to. 这里编译器可以知道q指向的数组类型。 But not because you told malloc the total size (that information is not returned from the malloc call) but because m is part of the type specification of q . 但不是因为你告诉malloc总大小(该信息不是从malloc调用返回的),而是因为mq类型规范的一部分。

The C Standard is a bit loosey-goosey when it comes to the definition of certain terms in certain contexts. 当涉及某些语境中某些术语的定义时,C标准有点松散。 Given something like: 给出如下内容:

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

there's no really clear indication of what storage is occupied by objects *q1 or *q2, nor by q1->y or q2->y. 没有确切的迹象表明对象* q1或* q2占用的存储空间,也没有q1-> y或q2-> y占用的存储空间。 If *q1 will never be accessed afterward, then q2->y may be treated as a uint16_t[509], but writing to *q1 will trash the contents of q2->y[254] and above, and writing q2->y[254] and above will trash *q1. 如果之后永远不会访问* q1,那么q2-> y可以被视为uint16_t [509],但写入* q1会废弃q2-> y [254]及以上的内容,并写入q2-> y [254]以上将废弃* q1。 Since a compiler will generally have no way of knowing what will happen to *q1 in the future, it will have no way of sensibly reporting a size for q2->y. 由于编译器通常无法知道将来会发生什么* q1,因此无法合理地报告q2-> y的大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 具有灵活数组成员的结构的大小 - Size of a struct with flexible array member 为什么对具有灵活数组成员的结构的初始化无效但对固定大小的数组成员有效? - Why is this initialization of a structure with a flexible array member invalid but valid with an fixed size array member? 创建大小为零的灵活数组成员合法吗? - Is it legal to create a flexible array member of size zero? 我不明白为什么我的阵列大小不正确 - I can't understand why i have the wrong size of my array 为什么我不能初始化我的数组? - Why can't I initialize my array? 给定结构中灵活数组成员的绝对地址,我如何获得该结构的绝对地址? - Given the absolute address of the flexible array member in a struct, how can I get the absolute address of the struct? 为什么我不能创建一个大小由全局变量确定的数组? - Why can't I create an array with size determined by a global variable? 为什么灵活数组成员的静态初始化工作? - Why does static initialization of flexible array member work? 为什么单个(灵活数组)成员结构声明仅由 [0] 支持而不是 []? - Why single (flexible array) member struct declaration is only supported by [0] and not []? 具有灵活数组成员的结构的“数组” - “Array” of struct with flexible array member
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM