Strange values while initializing array using designated initializers

前端 未结 5 1071
迷失自我
迷失自我 2021-02-07 05:57

When I initialize the array below all the output looks ok except for values[3]. For some reason values[3] initialized as values[0]+values[5]

相关标签:
5条回答
  • 2021-02-07 06:10

    This has nothing to do with designated initializers as such. It is the same bug as you'd get when attempting something like this:

    int array[10] = {5, array[0]};
    

    The order in which initialization list expressions are executed is simply unspecified behavior. Meaning it is compiler-specific, undocumented and should never be relied upon:

    C11 6.7.9/23

    The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.

    Since you are using array items to initialize other array members, it means that you must change your code to run-time assignment instead of initialization.

      int values[10];
    
      values[2] = -100;
      values[5] = 350;
      values[3] = values[0] + values[5];
      ...
    

    As a side-effect, your program will now also be far more readable.

    0 讨论(0)
  • 2021-02-07 06:11
    int values[10] = { 
        [0]=197,[2]=-100,[5]=350,
        [3]=values[0] + values[5],
        [9]= values[5]/10
    };
    

    edit:

    The ISO C99 standard, section 6.7.8 (Initialization) specifies that

    The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject;132) all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration

    But as Shafik pointed out, the evaluation order doesnt have to match the initialization order

    Which means values[0] + values[5] may read garbage values from:

    • values[0]
    • values[5] (this is what happen in your case)
    • both
    • none of them
    0 讨论(0)
  • 2021-02-07 06:18

    It looks like you are subject to unspecified behavior here, since the order of evaluation of the initialization list expressions is unspecified, from the draft C99 standard section 6.7.8:

    The order in which any side effects occur among the initialization list expressions is unspecified.133)

    and note 133 says:

    In particular, the evaluation order need not be the same as the order of subobject initialization.

    As far as I can tell, the normative text that backs up note 133 would be from section 6.5:

    Except as specified later [...] the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.

    and we can see that an intializer is a full-expression from 6.8 (emphasis mine):

    A full expression is an expression that is not part of another expression or of a declarator. Each of the following is a full expression: an initializer; [...]

    After looking back at one of my old C++ answers that covered sequence points within an initializer and which places the full-expression in a different place then I originally concluded, I realized the grammar in 6.7.8 contained initializer twice:

    initializer:
        assignment-expression
        { initializer-list }
        { initializer-list , }
    initializer-list:
        designationopt initializer
        initializer-list , designationopt initializer
    

    I originally did not notice this and thought the statement on full-expressions applied to the top element in the above grammar.

    I now believe like C++ the full-expression applies to each initializer within the initializer-list which make my previous analysis incorrect.

    Defect report 439 confirmed my suspicion that this was indeed the case, it contains the following example:

    #include <stdio.h>
    
    #define ONE_INIT      '0' + i++ % 3
    #define INITIALIZERS      [2] = ONE_INIT, [1] = ONE_INIT, [0] = ONE_INIT
    
    int main()
    {
        int i = 0;
        char x[4] = { INITIALIZERS }; // case 1
        puts(x);
        puts((char [4]){ INITIALIZERS }); // case 2
        puts((char [4]){ INITIALIZERS } + i % 2); // case 3
    }
    

    and it says:

    In every use of the INITIALIZERS macro, the variable i is incremented three times. In cases 1 and 2, there is no undefined behavior, because the increments are in expressions that are indeterminately sequenced with respect to one another, not unsequenced.

    so each intializer within INITIALIZERS is a full-expression.

    Since this defect report is against C11 it is worth noting that C11 is more verbose then C99 in the normative text on this issue and it says:

    The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.152)

    There is undefined behavior in the case where the following expressions are evaluated before the respective elements in values are assigned to:

     values[0] + values[5]
    

    or:

     values[5]/10
    

    This is undefined behavior since using an indeterminate value invokes undefined behavior.

    In this specific case the simplest work-around would be to perform the calculations by hand:

    int values[10] = { 
        [0]=197,[2]=-100,[5]=350,
        [3]= 197 + 350,
        [9]= 350/10
    };
    

    There are other alternatives such as doing the assignments to element 3 and 9 after the initialization.

    0 讨论(0)
  • 2021-02-07 06:24

    Try this code:

    int values[10];
    values[0]=197;
    values[2]=-100;
    values[5]=350;
    values[3]=values[0]+values[5];
    values[9]=values[5]/10;
    

    And then you print the array like you've done.

    0 讨论(0)
  • 2021-02-07 06:34

    This is the first time that I have seen something initialized that way, but I figured that the behavior you are seeing had to do with accessing a piece of the array that has not yet been initialized. So I built it using GCC 4.6.3 on a 32-bit Ubuntu 12.04 system. In my environment, I got different results than you.

    gcc file.c -o file
    
    ./file
    values[0] = 197
    values[1] = 0
    values[2] = -100
    values[3] = 197
    values[4] = 0
    values[5] = 350
    values[6] = 0
    values[7] = 0
    values[8] = 0
    values[9] = 35
    
    
    objdump -d file > file.asm
    
    cat file.asm     (relevant portion posted below)
    
    080483e4 <main>:
     80483e4:   55                      push   %ebp
     80483e5:   89 e5                   mov    %esp,%ebp
     80483e7:   57                      push   %edi
     80483e8:   53                      push   %ebx
     80483e9:   83 e4 f0                and    $0xfffffff0,%esp
     80483ec:   83 ec 40                sub    $0x40,%esp
     80483ef:   8d 5c 24 14             lea    0x14(%esp),%ebx
     80483f3:   b8 00 00 00 00          mov    $0x0,%eax
     80483f8:   ba 0a 00 00 00          mov    $0xa,%edx
     80483fd:   89 df                   mov    %ebx,%edi
     80483ff:   89 d1                   mov    %edx,%ecx
     8048401:   f3 ab                   rep stos %eax,%es:(%edi)   <=====
     8048403:   c7 44 24 14 c5 00 00    movl   $0xc5,0x14(%esp)
     804840a:   00 
     804840b:   c7 44 24 1c 9c ff ff    movl   $0xffffff9c,0x1c(%esp)
     8048412:   ff
     8048413:   8b 54 24 14             mov    0x14(%esp),%edx
     8048417:   8b 44 24 28             mov    0x28(%esp),%eax
     804841b:   01 d0                   add    %edx,%eax
     804841d:   89 44 24 20             mov    %eax,0x20(%esp)
     8048421:   c7 44 24 28 5e 01 00    movl   $0x15e,0x28(%esp)
     8048428:   00 
     8048429:   8b 4c 24 28             mov    0x28(%esp),%ecx
     804842d:   ba 67 66 66 66          mov    $0x66666667,%edx
     8048432:   89 c8                   mov    %ecx,%eax
     8048434:   f7 ea                   imul   %edx
     8048436:   c1 fa 02                sar    $0x2,%edx
     8048439:   89 c8                   mov    %ecx,%eax
     804843b:   c1 f8 1f                sar    $0x1f,%eax
    

    I've identified a key line in the above output that I think marks the difference between what yours generated and what mine generated (marked with <======). Before specific array elements are initialized with the values you specified, mine is zeroing the contents of the array. The specific initialization of array elements occurs after this.

    Given the above behavior, I do not think that it is unreasonable to hypothesize that yours did not zero the array contents prior to initializing specific elements of the array. As to why the difference in behavior? I can only speculate; but my first guess is that we are using two different compiler versions.

    Hope this helps.

    0 讨论(0)
提交回复
热议问题