3

I thought I knew C pretty well, but I'm confused by the following code:

typedef struct {
    int type;
} cmd_t;

typedef struct {
    int size;
    char data[];
} pkt_t;

int func(pkt_t *b)
{
    int *typep;
    char *ptr;

    /* #1: Generates warning */
    typep = &((cmd_t*)(&(b->data[0])))->type;

    /* #2: Doesn't generate warning */
    ptr = &b->data[0];
    typep = &((cmd_t*)ptr)->type;

    return *typep;
}

When I compile with GCC, I get the "dereferencing type-punned pointer will break strict-aliasing rules" warning.

  1. Why am I getting this warning at all? I'm dereferencing at char array. Casting a char * to anything is legal. Is this one of those cases where an array is not exactly the same as a pointer?

  2. Why aren't both assignments generating the warning? The 2nd assignment is the equivalent of the first, isn't it?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
JayM
  • 4,798
  • 1
  • 21
  • 15

1 Answers1

0

When strict aliasing is turned on, the compiler is allowed to assume that two pointers of different type (char* vs cmt_t* in this instance) will not point to the same memory location. This allows for a greater range of optimizations which you would otherwise not want to be applied if they do indeed point to the same memory location. Various examples/horror-stories can be found in this question.

This is why, under strict-aliasing, you have to be careful how you do type punning. I believe that the standard doesn't allow for any type-puning what-so-ever (don't quote me on that) but most compilers have exemption for unions (my google-fu is failing in turning up the relevant manual pages):

union float_to_int {
    double d;
    uint64_t i;
};

union float_to_int ftoi;
ftoi.d = 1.0;
... = ftoi.i;

Unfortunately, this doesn't quite work for your situation as you would have to memcpy the content of the array into the union which is less then ideal. A simpler approach would be to simply to turn off strict-aliasing via the -fno-strict-aliasing switch. This will ensure that your code is correct and it's unlikely to have a significant performance impact (do measure if performance matters).

As for why the warning doesn't show up when the line is broken up, I don't know. Chances are that the modifications to the source code manages to confuse the compiler's static analysis pass enough that it doesn't see the type-punning. Note that the static analysis pass responsible for detecting type-punning is unrelated and doesn't talk to the various optimization passes that assume strict-aliasing. You can think of any static analysis done by compilers (unless otherwise specified) as a best-effort type of thing. In other words, the absence of warning doesn't mean that there are no errors which means that simply breaking up the line doesn't magically make your type-punning safe.

Community
  • 1
  • 1
Ze Blob
  • 2,817
  • 22
  • 16