4

Let's say that both int and float are 4-byte aligned. According to ISO C99 6.3.2.3 p.7:

A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined.

According to this, the following code should not invoke UB:

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

int main(void)
{
    int i = 7;
    float *f = (float *) &i;
    exit(0);
}

I noticed that GCC is not always good at catching strict aliasing rules violation attempts but here in can correctly warn that dereferencing *f will break will break strict-aliasing rules what is illegal according to 6.5 p. 7:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.
$ gcc  -Wall -fstrict-aliasing -Wstrict-aliasing=2 so1.c -Wcast-align
so1.c: In function ‘main’:
so1.c:7:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
so1.c:7:9: warning: unused variable ‘f’ [-Wunused-variable]

So does it mean that this code may invoke UB only when *f will be dereferenced and it's 100% correct in current shape?

EDIT:

One more thing - would this cast be also legal if float had a different size from int but alignment would still be the same for both types? I think yes because this is what C standard says but I don't understand why alignment is the most important here and not size. Why does alignment matters when converting pointers and only when dereferencing?

user1042840
  • 1,925
  • 2
  • 16
  • 32
  • 1
    I believe it's UB because `float` matches none of the conditions in your list. However, you can muck about with a union. (Or just use `-fno-strict-aliasing` like a normal human being.) – tmyklebu Mar 09 '15 at 16:25
  • But it's UB only to dereference *f? – user1042840 Mar 09 '15 at 16:26
  • Whoops, yeah. What you've done is fine. But dereferencing `f` is not OK. – tmyklebu Mar 09 '15 at 16:27
  • @user1042840: Should only be UB if you access the value through `f`, yeah. Kinda pointless to have that pointer without dereferencing it though. :) – Ulfalizer Mar 09 '15 at 16:27
  • Cool, I just want to make sure I understand everything. One more thing - would this cast be also legal if float had a different size from int but alignment would still be the same for both types? – user1042840 Mar 09 '15 at 16:28
  • 1
    @The Paramagnetic Croissant OP appears to only copy the address of `i` and not the contents. Why is _that_ UB? – chux - Reinstate Monica Mar 09 '15 at 16:39
  • @chux sorry, I was referring to the act of dereferencing. – The Paramagnetic Croissant Mar 09 '15 at 16:43
  • @chux: exactly, that's my point - 6.3.2.3 p.7 doesn't mention dereferencing, but it says that if two pointers have different `alignment` if it's already an UB to convert one into each other, so in theory anything can happen on this stage even without dereferencing. – user1042840 Mar 09 '15 at 16:43
  • If 2 types have same alignment requirements, _casting_ is OK regardless of type sizes per "C99 6.3.2.3 p.7". If types have different sizes, (yet same alignment requirements), pointer arithmetic is a problem as `float *f = (float *) &i; &f[1]` may be UB. The end result is the _casting_ of like aligned types will not cause the problem so much as the typical _next_ operations of de-referencing and pointer arithmetic. – chux - Reinstate Monica Mar 09 '15 at 16:51
  • 1
    There exist(ed) machine architectures where merely loading an unaligned address into a register meant to index memory would cause a hardware trap. But I don't think any contemporary architecture has that limitation – rep_movsd Mar 09 '15 at 17:16
  • @rep_movsd: Some systems may use a shorter representation for pointers which are known to be aligned on coarser boundaries. For example, a compiler for a 64-bit platform whose process space would be limited to 16GB could have pointers to word-aligned objects be 32 bits even though `void*` was 64 bits. If code held a lot of references to `int`-aligned objects, such a design could cut in half the amount of memory/cache needed to hold pointers, potentially offering a significant performance win. – supercat May 08 '15 at 15:27

0 Answers0