Overlapping packages and pointers, C and C ++

union vec { #pragma pack(push,1) struct { float x, y, z; } #pragma pack(pop) float vals[3]; }; 

Consider the above definition. (Anonymous unions in C99 aside)

I believe that this answer probably gives different answers depending on the choice of compiler, choice of language and choice of standard.

  • I believe that I am guaranteed (through the #pragma compiler documentation, not the language guarantees) that sizeof(vec) == 3*sizeof(float)
  • As such, I believe that I am guaranteed that &vec.x == &vec.vals[0] , ect.
  • However, I'm not sure if it is legal (that is, not allowed with strict aliases) to write from vx and then read from v.vals[0]

Having postponed the packaging, I believe that the corresponding wording (at least according to the C99 standard):

  • a type compatible with an efficient object type,

  • a collection or type of association that includes one of the above types among its members (including, recursively, a subaggregate or contains union member), or

+5
source share
1 answer
  • I believe that I am guaranteed (through the #pragma compiler documentation, not the language guarantee) that sizeof (vec) == 3 * sizeof (float)

Yes, that’s right if you completely disable the #pragma delayed add-on.


  1. As such, I believe that I am guaranteed that & vec.x == & vec.vals [0], ect.

This is guaranteed regardless of padding, because there can never be any padding at the beginning of a structure / union. See, for example, C11 6.7.2.1 §15:

A structural object may have an unnamed addition, but not at the beginning.

This is true for all versions of the C standard, and, as far as I know, also for all versions of the C ++ standard.


  1. However, I am not sure if it is legal (that is, not allowed with strict aliases) to write from vx and then read from v.vals [0]

This is good in C, but undefined behavior in C ++.

In C operator . / -> guarantees this, C11 6.5.2.3:

Postfix expression followed by. An operator and identifier denotes a member object of a structure or union. The value has a named member value, 95) and is an lvalue if the first expression is an lvalue.

If footnote 95 (informative rather than normative) reads:

95) If the element used to read the contents of the union object does not match the element that was last used to store the value in the object, the corresponding part of the representation of the value object is reinterpreted as representing the object in a new type, as described in 6.2.6 (process, sometimes called the "type" pun intended). This may be a trap view.

C ++ does not have such guarantees, so the “punning type” through union is undefined behavior in C ++. This is the main difference between the two languages.

In addition, C has the concept of a common initial sequence for joins, also specified in C11 6.5.2.3:

To simplify the use of unions, there is one special guarantee: if the connection contains several structures that have a common initial sequence (see below), and if the union object currently contains one of these structures, it is allowed to check the common initial part of any of them anywhere where the declaration of the completed join type is visible. Two structures have a common initial sequence if the corresponding members have compatible types (and for bit fields of the same width) for a sequence of one or more initial elements.


It is true that the array and structure in your example may be a pseudonym because of the part that you indicated "aggregate or union type that includes one of the above types among its members." Thus, writing to the structure and then reading this data through the array does not violate strict anti-aliasing, neither in C nor in C ++.

However, C ++ has the concept of an "active member" when working with unions, so in C ++ it would give poorly defined behavior for other reasons than imposing aliases, namely, C ++ guaranteed that the last written member of the union could be safe to read.

+6
source

Source: https://habr.com/ru/post/1271981/


All Articles