4

any_t is any type (int, struct something, …).

Consider this structure:

struct my_struct {
    any_t val,
    any_t array[10]
}

If I define a variable v:

struct my_struct v;

Is it safe to use &v.val as an array of 11 any_t items?

any_t *p = &v.val;
f(p[0]);
f(p[5]);
f(p[10]);

Is it guaranteed no padding will be added between val and array?

rom1v
  • 2,752
  • 3
  • 21
  • 47
  • 3
    You could use #pragma pack(1) to guarantee no spacing between members – Kevin Jul 22 '13 at 13:10
  • http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member worth reading. – yeyo Jul 22 '13 at 13:19

3 Answers3

7

From the C standard alone, it is not safe to use &v.val as an array of 11 any_t for these reasons:

  • The C standard permits unnamed internal padding in a struct: C 2011 (N1570) 6.7.2.1 15, “There may be unnamed padding within a structure object, but not at its beginning.” It would be unusual for a C implementation to insert padding between val and array, because alignment requirements would not require it, but it is permitted and it is conceivably beneficial in certain circumstances, such as causing array to be better aligned for performance (rather than necessity).
  • Even if there were a guarantee that the spacing of val and the array elements were the same as an array of 11 any_t, there is no guarantee that pointer arithmetic works. C 2011 (N1570) 6.5.6 8 defines pointer arithmetic (including array indexing), and it only requires that arithmetic work within an array (including one notional element at the end). Some C implementations use base-and-offset addressing. In such cases, a base address for val might fail to support offsets that extend into array.
  • Even if a C implementation uses simple flat addressing, its optimizer is permitted to make deductions based on the C standard. The optimizer can, in theory, see that a pointer is derived from the address of val and therefore cannot (in accordance with the C standard) be used to address anything in array. E.g., if you did any_t *p = &v.val; p[i] = 3; v.array[j] = 4;, the optimizer may treat the assignments to v.array[j] and p[i] as independent and perform them in any order, even though you may have set i and j so that they would point to the same element.
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • The standard seems a little bit murky with regard to what an implementation could or could not legitimately do with regard to pointers that extend beyond the declared size of an array; as yet I'm unaware of anything that either justifies or deprecates the "traditional" version of the "struct hack" (using a size-one array). With regard to your latter points, however, I would think that if a struct were within malloc'ed space, casting an address of an element to a `void*` and then back to its element type should guarantee that things should work if things are in fact located in the right place. – supercat Jul 22 '13 at 15:29
  • Unless the particular implementation is specified, I don't think there's any portable way to use array-ish accessing for structure members unless one has defines an array of offsets to expressly-listed struct members (among other things, there's no guarantee that members will be equally spaced), but I think that if a struct is within malloc'ed space, it's guaranteed to have its members overlap the appropriate bytes therein. – supercat Jul 22 '13 at 15:34
  • It is incorrect to use a model about address space to reason about C semantics (e.g., given that members will be at certain byte offsets, therefore they form an array) because the C standard does not define such a model. To reason properly, you must deduce behavior from the statements in the standard. As I noted, the optimizer can do unexpected things. Even if you allocate space with `malloc`, set `v` to point to it, and all the elements are at the expected offsets, if you set `any_t *p = &v->val`, then the optimizer is permitted to behave as if `p[i]` is not `v->array[j]`. – Eric Postpischil Jul 22 '13 at 15:41
1

No, nothing is guaranteed by the standard. Your specific compiler implementation may certainly make guarantees beyond those made by the standard, though. Check your documentation for all the details.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • Alignment requirements are not relevant. The `val` and `array` members would have the same alignment requirements, so padding for `val` caused by alignment requirements would be the same as padding for `array` elements caused by alignment requirements. The standard permits unnamed padding between `val` and `array`, but an implementation would not be inserting padding for alignment requirements. (Hypothetically, it might insert padding for alignment benefits, e.g., if it would help align `array` to a cache line, and the compiler has reason to believe that would be beneficial.) – Eric Postpischil Jul 22 '13 at 13:27
  • Fair enough; second sentence removed. – Carl Norum Jul 22 '13 at 13:31
0

Well your definition of the struct is about 2 fields, so your compailer may not use it as an arry of 11 elements, so you will have to fill your fileds as they are decleared.