Undefined behavior from pointer math on a C++ array

前端 未结 3 1144
逝去的感伤
逝去的感伤 2021-02-05 06:36

Why the output of this program is 4?

#include 

int main()
{
    short A[] = {1, 2, 3, 4, 5, 6};
    std::cout << *(short*)((c         


        
3条回答
  •  故里飘歌
    2021-02-05 07:09

    This is arguably a bug in GCC.

    First, it is to be noted that your code is invoking undefined behavior, due to violation of the rules of strict aliasing.

    With that said, here's why I consider it a bug:

    1. The same expression, when first assigned to an intermediate short or short *, causes the expected behavior. It's only when passing the expression directly as a function argument, does the unexpected behavior manifest.

    2. It occurs even when compiled with -O0 -fno-strict-aliasing.

    I re-wrote your code in C to eliminate the possibility of any C++ craziness. Your question is was tagged c after all! I added the pshort function to ensure that the variadic nature printf wasn't involved.

    #include 
    
    static void pshort(short val)
    {
        printf("0x%hx ", val);
    }
    
    int main(void)
    {
        short A[] = {1, 2, 3, 4, 5, 6};
    
    #define EXP ((short*)((char*)A + 7))
    
        short *p = EXP;
        short q = *EXP;
    
        pshort(*p);
        pshort(q);
        pshort(*EXP);
        printf("\n");
    
        return 0;
    }
    

    After compiling with gcc (GCC) 7.3.1 20180130 (Red Hat 7.3.1-2):

    gcc -O0 -fno-strict-aliasing -g -Wall -Werror  endian.c
    

    Output:

    0x500 0x500 0x4
    

    It appears that GCC is actually generating different code when the expression is used directly as an argument, even though I'm clearly using the same expression (EXP).

    Dumping with objdump -Mintel -S --no-show-raw-insn endian:

    int main(void)
    {
      40054d:   push   rbp
      40054e:   mov    rbp,rsp
      400551:   sub    rsp,0x20
        short A[] = {1, 2, 3, 4, 5, 6};
      400555:   mov    WORD PTR [rbp-0x16],0x1
      40055b:   mov    WORD PTR [rbp-0x14],0x2
      400561:   mov    WORD PTR [rbp-0x12],0x3
      400567:   mov    WORD PTR [rbp-0x10],0x4
      40056d:   mov    WORD PTR [rbp-0xe],0x5
      400573:   mov    WORD PTR [rbp-0xc],0x6
    
    #define EXP ((short*)((char*)A + 7))
    
        short *p = EXP;
      400579:   lea    rax,[rbp-0x16]             ; [rbp-0x16] is A
      40057d:   add    rax,0x7
      400581:   mov    QWORD PTR [rbp-0x8],rax    ; [rbp-0x08] is p
        short q = *EXP;
      400585:   movzx  eax,WORD PTR [rbp-0xf]     ; [rbp-0xf] is A plus 7 bytes
      400589:   mov    WORD PTR [rbp-0xa],ax      ; [rbp-0xa] is q
    
        pshort(*p);
      40058d:   mov    rax,QWORD PTR [rbp-0x8]    ; [rbp-0x08] is p
      400591:   movzx  eax,WORD PTR [rax]         ; *p
      400594:   cwde   
      400595:   mov    edi,eax
      400597:   call   400527 
        pshort(q);
      40059c:   movsx  eax,WORD PTR [rbp-0xa]      ; [rbp-0xa] is q
      4005a0:   mov    edi,eax
      4005a2:   call   400527 
        pshort(*EXP);
      4005a7:   movzx  eax,WORD PTR [rbp-0x10]    ; [rbp-0x10] is A plus 6 bytes ********
      4005ab:   cwde   
      4005ac:   mov    edi,eax
      4005ae:   call   400527 
        printf("\n");
      4005b3:   mov    edi,0xa
      4005b8:   call   400430 
    
        return 0;
      4005bd:   mov    eax,0x0
    }
      4005c2:   leave  
      4005c3:   ret
    

    • I get the same result with GCC 4.9.4 and GCC 5.5.0 from Docker hub

提交回复
热议问题