Do I have a gcc optimization bug or a C code problem?

前端 未结 9 2119
借酒劲吻你
借酒劲吻你 2021-02-06 00:57

Test the following code:

#include 
#include 
main()
{
    const char *yytext=\"0\";
    const float f=(float)atof(yytext);
    siz         


        
相关标签:
9条回答
  • 2021-02-06 01:31

    Aside the pointer alignments, you're expecting that sizeof(size_t)==sizeof(float). I don't think it is (on 64-bit Linux size_t should be 64 bits but float 32 bits), meaning your code will read something uninitialized.

    0 讨论(0)
  • 2021-02-06 01:32

    This is no longer allowed according to C99 rules on pointer aliasing. Pointers of two different types cannot point to the same location in memory. The exceptions to this rule are void and char pointers.

    So in your code where you are casting to a pointer of size_t, the compiler can choose to ignore this. If you want to get the float value as a size_t, just assign it and the float will be cast (truncated not rounded) as such:

    size_t size = (size_t)(f); // this works

    This is commonly reported as a bug, but in fact really is a feature that allows optimizers to work more efficiently.

    In gcc you can disable this with a compiler switch. I beleive -fno_strict_aliasing.

    0 讨论(0)
  • 2021-02-06 01:38

    In the C99 standard, this is covered by the following rule in 6.5-7:

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

    • 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.

    The last item is why casting first to a (char*) works.

    0 讨论(0)
  • 2021-02-06 01:44

    I tested your code with: "i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5465)"

    and there was no Problem. Output:

    t should be 0 but is 0
    

    So there isn't a bug in you code. That doesn't mean that it is good code. But I would add the returntype of the main-function and the "return 0;" at the end of the function.

    0 讨论(0)
  • 2021-02-06 01:45

    It is bad C code :-)

    The problematic part is that you access one object of type float by casting it to an integer pointer and dereferencing it.

    This breaks the aliasing rule. The compiler is free to assume that pointers to different types such as float or int don't overlap in memory. You've done exactly that.

    What the compiler sees is that you calculate something, store it in the float f and never access it anymore. Most likely the compiler has removed part of the code and the assignment has never happend.

    The dereferencing via your size_t pointer will in this case return some uninitialized garbage from the stack.

    You can do two things to work-around this:

    1. use a union with a float and a size_t member and do the casting via type punning. Not nice but works.

    2. use memcopy to copy the contents of f into your size_t. The compiler is smart enough to detect and optimize this case.

    0 讨论(0)
  • Use the compiler flag -fno-strict-aliasing.

    With strict aliasing enabled, as it is by default for at least -O3, in the line:

    size_t t = *((size_t*)&f);
    

    the compiler assumes that the size_t* does NOT point to the same memory area as the float*. As far as I know, this is standards-compliant behaviour (adherence with strict aliasing rules in the ANSI standard start around gcc-4, as Thomas Kammeyer pointed out).

    If I recall correctly, you can use an intermediate cast to char* to get around this. (compiler assumes char* can alias anything)

    In other words, try this (can't test it myself right now but I think it will work):

    size_t t = *((size_t*)(char*)&f);
    
    0 讨论(0)
提交回复
热议问题