gcc, strict-aliasing, and casting through a union

后端 未结 7 579
时光说笑
时光说笑 2020-11-28 06:22

Do you have any horror stories to tell? The GCC Manual recently added a warning regarding -fstrict-aliasing and casting a pointer through a union:

[.

7条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-11-28 07:16

    The fact that GCC is warning about unions doesn't necessarily mean that unions don't currently work. But here's a slightly less simple example than yours:

    #include 
    
    struct B {
        int i1;
        int i2;
    };
    
    union A {
        struct B b;
        double d;
    };
    
    int main() {
        double d = 3.0;
        #ifdef USE_UNION
            ((union A*)&d)->b.i2 += 0x80000000;
        #else
            ((int*)&d)[1] += 0x80000000;
        #endif
        printf("%g\n", d);
    }
    

    Output:

    $ gcc --version
    gcc (GCC) 4.3.4 20090804 (release) 1
    Copyright (C) 2008 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    
    $ gcc -oalias alias.c -O1 -std=c99 && ./alias
    -3
    
    $ gcc -oalias alias.c -O3 -std=c99 && ./alias
    3
    
    $ gcc -oalias alias.c -O1 -std=c99 -DUSE_UNION && ./alias
    -3
    
    $ gcc -oalias alias.c -O3 -std=c99 -DUSE_UNION && ./alias
    -3
    

    So on GCC 4.3.4, the union "saves the day" (assuming I want the output "-3"). It disables the optimisation that relies on strict aliasing and that results in the output "3" in the second case (only). With -Wall, USE_UNION also disables the type-pun warning.

    I don't have gcc 4.4 to test, but please give this code a go. Your code in effect tests whether the memory for d is initialised before reading back through a union: mine tests whether it is modified.

    Btw, the safe way to read half of a double as an int is:

    double d = 3;
    int i;
    memcpy(&i, &d, sizeof i);
    return i;
    

    With optimisation on GCC, this results in:

        int thing() {
    401130:       55                      push   %ebp
    401131:       89 e5                   mov    %esp,%ebp
    401133:       83 ec 10                sub    $0x10,%esp
            double d = 3;
    401136:       d9 05 a8 20 40 00       flds   0x4020a8
    40113c:       dd 5d f0                fstpl  -0x10(%ebp)
            int i;
            memcpy(&i, &d, sizeof i);
    40113f:       8b 45 f0                mov    -0x10(%ebp),%eax
            return i;
        }
    401142:       c9                      leave
    401143:       c3                      ret
    

    So there's no actual call to memcpy. If you aren't doing this, you deserve what you get if union casts stop working in GCC ;-)

提交回复
热议问题