1

Following code gives error when compiled with gcc version 4.1 and with -O2 flag. It compiles fine with gcc version 4.4, 4.5 etc.

The error is: warning: dereferencing type-punned pointer will break strict-aliasing rules

void foo(void **a = NULL);

int main()
{
    int *a;
    foo((void **)&a);  //I get above error here

    cout << "a[0] " << *a << endl;
    cout << "a[1] " << *(a+1) << endl;
    cout << "a[2] " << *(a+2) << endl;
    return 0;
}

void foo(void **a)
{
    int  b[3];
    b[0] = 10; b[1] = 20; b[2] = 35;
    if(a != NULL) {
         *a = (char *)malloc(20);
         memcpy((char *)(*a),  &b, 12);
    }

}

Now to avoid this can I program like given below. Is this is good solution to avoid this warning? I am able to avoid this warning in this code.

void foo2(char **a = NULL);

int main()
{
    char *a; 
    float c[3];
    foo2(&a);
    memcpy(&c, a, sizeof(c));
    cout << "c[0] " << *c << endl;
    cout << "c[1] " << *(c+1) << endl;
    cout << "c[2] " << *(c+2) << endl;
    return 0;
}

void foo2(char **a)
{
    float  c[3];
    c[0] = 10.123; c[1] = 2.3450; c[2] = 435.676;
    if(a != NULL) {
        *a = (char *)malloc(sizeof(c));
        memcpy((char *)(*a),  &c, sizeof(c));
    }
}
stev
  • 182
  • 1
  • 11

1 Answers1

4

The compiler is complaining that you are passing using a cast an int ** to a function accepting void ** and by the standard the compiler is not required to check that writes made through a void ** could affect an object of type int *.

An int * and a void * are different types and the compiler is not required to check about possible aliasing between them. As a practical concrete example consider:

int *a = &one_thing;
void **b = (void **)&a;
bar(a);
(*b) = &other_thing;
baz(a); // Here the compiler may assume `a` didn't change

Note also that the standard guarantees you can convert any pointer to an object to a void * and back safely, but your code however converts an int** to void** and back: this is not the same thing and is not guaranteed to be safe.

The memcpy case is interesting because formally the function takes void * values and a void * cannot be used to dereference anything. IIUC it is however acknowledged that memcpy will read/write one (unsigned) char at a time and therefore the compiler is forced to assume that after a memcpy call objects of any type that have been potentially written over could now have a new value (there is a special rule in the standard that says that a char * and an usigned char * can alias values of any type).

So yes, using memcpy you can do any nasty thing you like around the type system and the compiler is not allowed to ignore/reorder your write operations. I've read somewhere that compilers even detect memcpy calls and actually generate reasonable code so only the syntax (not the performance) will be horrible.

6502
  • 112,025
  • 15
  • 165
  • 265