Confusion about array initialization in C

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
32
down vote

favorite
4












In C language, if initialize an array like this:



int a[5] = 1,2;


then all the elements of the array that are not initialized explicitly will be initialized implicitly with zeroes.



But, if I initialize an array like this:



int a[5]=a[2]=1;

printf("%d %d %d %d %dn", a[0], a[1],a[2], a[3], a[4]);


output:



1 0 1 0 0


I don't understand, why does a[0] print 1 instead of 0? Is it undefined behaviour?



Note: This question was asked in an interview.










share|improve this question



















  • 11




    The expression a[2]=1 evaluates to 1.
    – tkausl
    10 hours ago






  • 7




    A very deep question. I wonder if the interviewer knows the answer themselves. I don't. Indeed ostensibly the value of the expression a[2] = 1 is 1, but I'm not sure if you are are allowed to take the result of a designated initialiser expression as the value of the first element. The fact you've added the lawyer tag means I think we need an answer citing the standard.
    – Bathsheba
    9 hours ago







  • 9




    Well if that's their favourite question, you may well have dodged a bullet. Personally I prefer a written programming exercise (with access to a compiler and debugger) to be taken over a few hours rather than "ace" style questions such as the above. I could conject an answer, but I don't think it would have any real factual basis.
    – Bathsheba
    9 hours ago







  • 1




    @Bathsheba I would do the opposite, as the answer here now answers both questions.
    – Kami Kaze
    8 hours ago






  • 1




    @Bathsheba would be the best. Still I would give the credit for the question to OP, as he came up with the topic. But this is not for me to decide just what I feel would be "the right thing".
    – Kami Kaze
    8 hours ago















up vote
32
down vote

favorite
4












In C language, if initialize an array like this:



int a[5] = 1,2;


then all the elements of the array that are not initialized explicitly will be initialized implicitly with zeroes.



But, if I initialize an array like this:



int a[5]=a[2]=1;

printf("%d %d %d %d %dn", a[0], a[1],a[2], a[3], a[4]);


output:



1 0 1 0 0


I don't understand, why does a[0] print 1 instead of 0? Is it undefined behaviour?



Note: This question was asked in an interview.










share|improve this question



















  • 11




    The expression a[2]=1 evaluates to 1.
    – tkausl
    10 hours ago






  • 7




    A very deep question. I wonder if the interviewer knows the answer themselves. I don't. Indeed ostensibly the value of the expression a[2] = 1 is 1, but I'm not sure if you are are allowed to take the result of a designated initialiser expression as the value of the first element. The fact you've added the lawyer tag means I think we need an answer citing the standard.
    – Bathsheba
    9 hours ago







  • 9




    Well if that's their favourite question, you may well have dodged a bullet. Personally I prefer a written programming exercise (with access to a compiler and debugger) to be taken over a few hours rather than "ace" style questions such as the above. I could conject an answer, but I don't think it would have any real factual basis.
    – Bathsheba
    9 hours ago







  • 1




    @Bathsheba I would do the opposite, as the answer here now answers both questions.
    – Kami Kaze
    8 hours ago






  • 1




    @Bathsheba would be the best. Still I would give the credit for the question to OP, as he came up with the topic. But this is not for me to decide just what I feel would be "the right thing".
    – Kami Kaze
    8 hours ago













up vote
32
down vote

favorite
4









up vote
32
down vote

favorite
4






4





In C language, if initialize an array like this:



int a[5] = 1,2;


then all the elements of the array that are not initialized explicitly will be initialized implicitly with zeroes.



But, if I initialize an array like this:



int a[5]=a[2]=1;

printf("%d %d %d %d %dn", a[0], a[1],a[2], a[3], a[4]);


output:



1 0 1 0 0


I don't understand, why does a[0] print 1 instead of 0? Is it undefined behaviour?



Note: This question was asked in an interview.










share|improve this question















In C language, if initialize an array like this:



int a[5] = 1,2;


then all the elements of the array that are not initialized explicitly will be initialized implicitly with zeroes.



But, if I initialize an array like this:



int a[5]=a[2]=1;

printf("%d %d %d %d %dn", a[0], a[1],a[2], a[3], a[4]);


output:



1 0 1 0 0


I don't understand, why does a[0] print 1 instead of 0? Is it undefined behaviour?



Note: This question was asked in an interview.







c arrays initialization language-lawyer






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 1 hour ago









Jesse de Bruijne

2,28231125




2,28231125










asked 10 hours ago









rsp

17.1k454113




17.1k454113







  • 11




    The expression a[2]=1 evaluates to 1.
    – tkausl
    10 hours ago






  • 7




    A very deep question. I wonder if the interviewer knows the answer themselves. I don't. Indeed ostensibly the value of the expression a[2] = 1 is 1, but I'm not sure if you are are allowed to take the result of a designated initialiser expression as the value of the first element. The fact you've added the lawyer tag means I think we need an answer citing the standard.
    – Bathsheba
    9 hours ago







  • 9




    Well if that's their favourite question, you may well have dodged a bullet. Personally I prefer a written programming exercise (with access to a compiler and debugger) to be taken over a few hours rather than "ace" style questions such as the above. I could conject an answer, but I don't think it would have any real factual basis.
    – Bathsheba
    9 hours ago







  • 1




    @Bathsheba I would do the opposite, as the answer here now answers both questions.
    – Kami Kaze
    8 hours ago






  • 1




    @Bathsheba would be the best. Still I would give the credit for the question to OP, as he came up with the topic. But this is not for me to decide just what I feel would be "the right thing".
    – Kami Kaze
    8 hours ago













  • 11




    The expression a[2]=1 evaluates to 1.
    – tkausl
    10 hours ago






  • 7




    A very deep question. I wonder if the interviewer knows the answer themselves. I don't. Indeed ostensibly the value of the expression a[2] = 1 is 1, but I'm not sure if you are are allowed to take the result of a designated initialiser expression as the value of the first element. The fact you've added the lawyer tag means I think we need an answer citing the standard.
    – Bathsheba
    9 hours ago







  • 9




    Well if that's their favourite question, you may well have dodged a bullet. Personally I prefer a written programming exercise (with access to a compiler and debugger) to be taken over a few hours rather than "ace" style questions such as the above. I could conject an answer, but I don't think it would have any real factual basis.
    – Bathsheba
    9 hours ago







  • 1




    @Bathsheba I would do the opposite, as the answer here now answers both questions.
    – Kami Kaze
    8 hours ago






  • 1




    @Bathsheba would be the best. Still I would give the credit for the question to OP, as he came up with the topic. But this is not for me to decide just what I feel would be "the right thing".
    – Kami Kaze
    8 hours ago








11




11




The expression a[2]=1 evaluates to 1.
– tkausl
10 hours ago




The expression a[2]=1 evaluates to 1.
– tkausl
10 hours ago




7




7




A very deep question. I wonder if the interviewer knows the answer themselves. I don't. Indeed ostensibly the value of the expression a[2] = 1 is 1, but I'm not sure if you are are allowed to take the result of a designated initialiser expression as the value of the first element. The fact you've added the lawyer tag means I think we need an answer citing the standard.
– Bathsheba
9 hours ago





A very deep question. I wonder if the interviewer knows the answer themselves. I don't. Indeed ostensibly the value of the expression a[2] = 1 is 1, but I'm not sure if you are are allowed to take the result of a designated initialiser expression as the value of the first element. The fact you've added the lawyer tag means I think we need an answer citing the standard.
– Bathsheba
9 hours ago





9




9




Well if that's their favourite question, you may well have dodged a bullet. Personally I prefer a written programming exercise (with access to a compiler and debugger) to be taken over a few hours rather than "ace" style questions such as the above. I could conject an answer, but I don't think it would have any real factual basis.
– Bathsheba
9 hours ago





Well if that's their favourite question, you may well have dodged a bullet. Personally I prefer a written programming exercise (with access to a compiler and debugger) to be taken over a few hours rather than "ace" style questions such as the above. I could conject an answer, but I don't think it would have any real factual basis.
– Bathsheba
9 hours ago





1




1




@Bathsheba I would do the opposite, as the answer here now answers both questions.
– Kami Kaze
8 hours ago




@Bathsheba I would do the opposite, as the answer here now answers both questions.
– Kami Kaze
8 hours ago




1




1




@Bathsheba would be the best. Still I would give the credit for the question to OP, as he came up with the topic. But this is not for me to decide just what I feel would be "the right thing".
– Kami Kaze
8 hours ago





@Bathsheba would be the best. Still I would give the credit for the question to OP, as he came up with the topic. But this is not for me to decide just what I feel would be "the right thing".
– Kami Kaze
8 hours ago













4 Answers
4






active

oldest

votes

















up vote
36
down vote













TL;DR: I don't think the behavior of int a[5]=a[2]=1; is well defined, at least in C99.



The funny part is that the only bit that makes sense to me is the part you're asking about: a[0] is set to 1 because the assignment operator returns the value that was assigned. It's everything else that's unclear.



If the code had been int a[5] = [2] = 1 , everything would've been easy: That's a designated initializer setting a[2] to 1 and everything else to 0. But with a[2] = 1 we have a non-designated initializer containing an assignment expression, and we fall down a rabbit hole.




Here's what I've found so far:




  • a must be a local variable.




    6.7.8 Initialization



    1. All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.



    a[2] = 1 is not a constant expression, so a must have automatic storage.




  • a is in scope in its own initialization.




    6.2.1 Scopes of identifiers



    1. Structure, union, and enumeration tags have scope that begins just after the appearance of
      the tag in a type specifier that declares the tag. Each enumeration constant has scope that
      begins just after the appearance of its defining enumerator in an enumerator list. Any
      other identifier has scope that begins just after the completion of its declarator.




    The declarator is a[5], so variables are in scope in their own initialization.




  • a is alive in its own initialization.




    6.2.4 Storage durations of objects



    1. An object whose identifier is declared with no linkage and without the storage-class
      specifier static has automatic storage duration.


    2. For such an object that does not have a variable length array type, its lifetime extends
      from entry into the block with which it is associated until execution of that block ends
      in
      any way. (Entering an enclosed block or calling a function suspends, but does not end,
      execution of the current block.) If the block is entered recursively, a new instance of the
      object is created each time. The initial value of the object is indeterminate. If an
      initialization is specified for the object, it is performed each time the declaration is
      reached in the execution of the block; otherwise, the value becomes indeterminate each
      time the declaration is reached.





  • There is a sequence point after a[2]=1.




    6.8 Statements and blocks



    1. 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; the expression in an expression
      statement; the controlling expression of a selection statement (if or switch); the
      controlling expression of a while or do statement; each of the (optional) expressions of
      a for statement; the (optional) expression in a return statement. The end of a full
      expression is a sequence point.




    Note that e.g. in int foo = 1, 2, 3 the 1, 2, 3 part is a brace-enclosed list of initializers, each of which has a sequence point after it.




  • Initialization is performed in initializer list order.




    6.7.8 Initialization



    1. Each brace-enclosed initializer list has an associated current object. When no
      designations are present, subobjects of the current object are initialized in order according
      to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. [...]

     



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




  • However, initializer expressions are not necessarily evaluated in order.




    6.7.8 Initialization



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




However, that still leaves some questions unanswered:




  • Are sequence points even relevant? The basic rule is:




    6.5 Expressions



    1. Between the previous and next sequence point an object shall have its stored value
      modified at most once by the evaluation of an expression. Furthermore, the prior value
      shall be read only to determine the value to be stored.



    a[2] = 1 is an expression, but initialization is not.



    This is slightly contradicted by Annex J:




    J.2 Undefined behavior



    • Between two sequence points, an object is modified more than once, or is modified
      and the prior value is read other than to determine the value to be stored (6.5).



    Annex J says any modification counts, not just modifications by expressions. But given that annexes are non-normative, we can probably ignore that.



  • How are the subobject initializations sequenced with respect to initializer expressions? Are all initializers evaluated first (in some order), then the subobjects are initialized with the results (in initializer list order)? Or can they be interleaved?



I think int a[5] = a[2] = 1 is executed as follows:



  1. Storage for a is allocated when its containing block is entered. The contents are indeterminate at this point.

  2. The (only) initializer is executed (a[2] = 1), followed by a sequence point. This stores 1 in a[2] and returns 1.

  3. That 1 is used to initialize a[0] (the first initializer initializes the first subobject).

But here things get fuzzy because the remaining elements (a[1], a[2], a[3], a[4]) are supposed to be initialized to 0, but it's not clear when: Does it happen before a[2] = 1 is evaluated? If so, a[2] = 1 would "win" and overwrite a[2], but would that assignment have undefined behavior because there is no sequence point between the zero initialization and the assignment expression? Are sequence points even relevant (see above)? Or does zero initialization happen after all initializers are evaluated? If so, a[2] should end up being 0.



Because the C standard does not clearly define what happens here, I believe the behavior is undefined (by omission).






share|improve this answer


















  • 1




    Instead of undefined I would argue that it's unspecified, which leave things open for interpretation by the implementations.
    – Some programmer dude
    7 hours ago










  • I don't think any of the stuff before your last horizontal rule is relevant to the question; the only issue is when the zero initialization happens (which the standard doesn't seem to say). The same issue would be raised by int a[5] = a[2] = a[3] ;
    – M.M
    7 hours ago











  • "we fall into a rabbit hole" LOL! Never heard that for an UB or unspecified stuff.
    – BЈовић
    6 hours ago










  • I think 6.7.8.19 can be read in two ways, both of which give "valid" outcomes, but both of which are undesirable readings. 1) We consider a[2] = 1 to initialize a[2] (even though it is explicitly not "an initializer" for a[2]) because it "sets an initial value" (as parenthetically mentioned in 5.1.2), which makes the outcome with a[2] == 1 well-defined (because it was "initialized explicitly" and so mustn't be set to 0). This is undesirable because it sneaks in initialization without an initializer, bypassing all the careful words about how the order of initializers is not specified...
    – Jeroen Mostert
    3 hours ago











  • ... or 2) we consider a[2] = 1 not to initialize by the narrow interpretation of "initialize" where an assignment does not "initialize", but then a[2] must be set to 0 by 6.7.8.19. This is undesirable because it clashes with the most obvious implementation of initialization (initialize the whole block to 0, then process initializers), puts an undue burden on the compiler, and makes the whole thing useless anyway. I would agree that by the way things are currently worded, the whole thing should probably be considered undefined by virtue of the standard not being explicit enough.
    – Jeroen Mostert
    3 hours ago


















up vote
14
down vote














I don't understand, why does a[0] print 1 instead of 0?




Presumably a[2]=1 initializes a[2] first, and the result of the expression is used to initialize a[0].



From N2176 (C17 draft):




6.7.9 Initialization



  1. 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. 154)



So it would seem that output 1 0 0 0 0 would also have been possible.



Conclusion: Don't write initializers that modifies the initialized variable on the fly.






share|improve this answer


















  • 1




    That part does not apply: There is only one initializer expression here, so it doesn't need to be sequenced with anything.
    – melpomene
    8 hours ago










  • @melpomene There is the ... expression which initializes a[2] to 0, and a[2]=1 sub-expression which initializes a[2] to 1.
    – user694733
    8 hours ago






  • 1




    ... is a braced initializer list. It is not an expression.
    – melpomene
    8 hours ago










  • @melpomene Ok, you may be right there. But I would still argue there are still 2 competing side-effects so that paragraph stands.
    – user694733
    8 hours ago










  • @Gerhardh Yeah, I started having doubts about that too. I removed it. Thanks.
    – user694733
    7 hours ago

















up vote
1
down vote













My Understanding is
a[2]=1 returns value 1 so code becomes



int a[5]=a[2]=1 --> int a[5]=1


int a[5]=1 assign value for a[0]=1



Hence it print 1 for a[0]



For example



char str[10]=‘H’,‘a’,‘i’;


char str[0] = ‘H’;
char str[1] = ‘a’;
char str[2] = ‘i;





share|improve this answer



























    up vote
    1
    down vote













    I try to give a short and simple answer to the puzzle: int a[5] = a[2] = 1 ;



    1. First a[2] = 1 is set. That means the array says: 0 0 1 0 0

    2. But behold, given that you did it in the brackets, which are used to initialize the array in order, it takes the first value (which is 1) and sets that to a[0]. It is as if int a[5] = a[2] ; would remain, where we already got a[2] = 2. The resulting array is now: 1 0 1 0 0

    I guess it has to do with declarations being done first (or even the last declaration being done first). Example: A = B = C = 5, it starts with C = 5, goes to B = C, then A = B.



    That raises the question how a[2] can be declared if the array should not exist yet. Well, it actually does the moment ... is read, which is logical, given that every value in that bracket must be assigned to an already allocated array anyway. It's special alias/syntax for array initiation.






    share|improve this answer




















    • Small info: Another example: int a[6] = a[3] = 1, a[4] = 2, a[5] = 3 ; - It goes from left to right, each starting with the internal declaration. It goes in 6 steps: 0 0 0 1 0 0, 1 0 0 1 0 0, 1 0 0 1 2 0, 1 2 0 1 2 0, 1 2 0 1 2 3, 1 2 3 1 2 3.
      – Battle
      4 hours ago











    • A = B = C = 5 is not a declaration (or initialization). It's a normal expression that parses as A = (B = (C = 5)) because the = operator is right associative. That doesn't really help with explaining how initialization works. The array actually starts existing when the block it is defined in is entered, which can be long before the actual definition is executed.
      – melpomene
      17 mins ago










    Your Answer





    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52307474%2fconfusion-about-array-initialization-in-c%23new-answer', 'question_page');

    );

    Post as a guest






























    4 Answers
    4






    active

    oldest

    votes








    4 Answers
    4






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    36
    down vote













    TL;DR: I don't think the behavior of int a[5]=a[2]=1; is well defined, at least in C99.



    The funny part is that the only bit that makes sense to me is the part you're asking about: a[0] is set to 1 because the assignment operator returns the value that was assigned. It's everything else that's unclear.



    If the code had been int a[5] = [2] = 1 , everything would've been easy: That's a designated initializer setting a[2] to 1 and everything else to 0. But with a[2] = 1 we have a non-designated initializer containing an assignment expression, and we fall down a rabbit hole.




    Here's what I've found so far:




    • a must be a local variable.




      6.7.8 Initialization



      1. All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.



      a[2] = 1 is not a constant expression, so a must have automatic storage.




    • a is in scope in its own initialization.




      6.2.1 Scopes of identifiers



      1. Structure, union, and enumeration tags have scope that begins just after the appearance of
        the tag in a type specifier that declares the tag. Each enumeration constant has scope that
        begins just after the appearance of its defining enumerator in an enumerator list. Any
        other identifier has scope that begins just after the completion of its declarator.




      The declarator is a[5], so variables are in scope in their own initialization.




    • a is alive in its own initialization.




      6.2.4 Storage durations of objects



      1. An object whose identifier is declared with no linkage and without the storage-class
        specifier static has automatic storage duration.


      2. For such an object that does not have a variable length array type, its lifetime extends
        from entry into the block with which it is associated until execution of that block ends
        in
        any way. (Entering an enclosed block or calling a function suspends, but does not end,
        execution of the current block.) If the block is entered recursively, a new instance of the
        object is created each time. The initial value of the object is indeterminate. If an
        initialization is specified for the object, it is performed each time the declaration is
        reached in the execution of the block; otherwise, the value becomes indeterminate each
        time the declaration is reached.





    • There is a sequence point after a[2]=1.




      6.8 Statements and blocks



      1. 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; the expression in an expression
        statement; the controlling expression of a selection statement (if or switch); the
        controlling expression of a while or do statement; each of the (optional) expressions of
        a for statement; the (optional) expression in a return statement. The end of a full
        expression is a sequence point.




      Note that e.g. in int foo = 1, 2, 3 the 1, 2, 3 part is a brace-enclosed list of initializers, each of which has a sequence point after it.




    • Initialization is performed in initializer list order.




      6.7.8 Initialization



      1. Each brace-enclosed initializer list has an associated current object. When no
        designations are present, subobjects of the current object are initialized in order according
        to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. [...]

       



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




    • However, initializer expressions are not necessarily evaluated in order.




      6.7.8 Initialization



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




    However, that still leaves some questions unanswered:




    • Are sequence points even relevant? The basic rule is:




      6.5 Expressions



      1. Between the previous and next sequence point an object shall have its stored value
        modified at most once by the evaluation of an expression. Furthermore, the prior value
        shall be read only to determine the value to be stored.



      a[2] = 1 is an expression, but initialization is not.



      This is slightly contradicted by Annex J:




      J.2 Undefined behavior



      • Between two sequence points, an object is modified more than once, or is modified
        and the prior value is read other than to determine the value to be stored (6.5).



      Annex J says any modification counts, not just modifications by expressions. But given that annexes are non-normative, we can probably ignore that.



    • How are the subobject initializations sequenced with respect to initializer expressions? Are all initializers evaluated first (in some order), then the subobjects are initialized with the results (in initializer list order)? Or can they be interleaved?



    I think int a[5] = a[2] = 1 is executed as follows:



    1. Storage for a is allocated when its containing block is entered. The contents are indeterminate at this point.

    2. The (only) initializer is executed (a[2] = 1), followed by a sequence point. This stores 1 in a[2] and returns 1.

    3. That 1 is used to initialize a[0] (the first initializer initializes the first subobject).

    But here things get fuzzy because the remaining elements (a[1], a[2], a[3], a[4]) are supposed to be initialized to 0, but it's not clear when: Does it happen before a[2] = 1 is evaluated? If so, a[2] = 1 would "win" and overwrite a[2], but would that assignment have undefined behavior because there is no sequence point between the zero initialization and the assignment expression? Are sequence points even relevant (see above)? Or does zero initialization happen after all initializers are evaluated? If so, a[2] should end up being 0.



    Because the C standard does not clearly define what happens here, I believe the behavior is undefined (by omission).






    share|improve this answer


















    • 1




      Instead of undefined I would argue that it's unspecified, which leave things open for interpretation by the implementations.
      – Some programmer dude
      7 hours ago










    • I don't think any of the stuff before your last horizontal rule is relevant to the question; the only issue is when the zero initialization happens (which the standard doesn't seem to say). The same issue would be raised by int a[5] = a[2] = a[3] ;
      – M.M
      7 hours ago











    • "we fall into a rabbit hole" LOL! Never heard that for an UB or unspecified stuff.
      – BЈовић
      6 hours ago










    • I think 6.7.8.19 can be read in two ways, both of which give "valid" outcomes, but both of which are undesirable readings. 1) We consider a[2] = 1 to initialize a[2] (even though it is explicitly not "an initializer" for a[2]) because it "sets an initial value" (as parenthetically mentioned in 5.1.2), which makes the outcome with a[2] == 1 well-defined (because it was "initialized explicitly" and so mustn't be set to 0). This is undesirable because it sneaks in initialization without an initializer, bypassing all the careful words about how the order of initializers is not specified...
      – Jeroen Mostert
      3 hours ago











    • ... or 2) we consider a[2] = 1 not to initialize by the narrow interpretation of "initialize" where an assignment does not "initialize", but then a[2] must be set to 0 by 6.7.8.19. This is undesirable because it clashes with the most obvious implementation of initialization (initialize the whole block to 0, then process initializers), puts an undue burden on the compiler, and makes the whole thing useless anyway. I would agree that by the way things are currently worded, the whole thing should probably be considered undefined by virtue of the standard not being explicit enough.
      – Jeroen Mostert
      3 hours ago















    up vote
    36
    down vote













    TL;DR: I don't think the behavior of int a[5]=a[2]=1; is well defined, at least in C99.



    The funny part is that the only bit that makes sense to me is the part you're asking about: a[0] is set to 1 because the assignment operator returns the value that was assigned. It's everything else that's unclear.



    If the code had been int a[5] = [2] = 1 , everything would've been easy: That's a designated initializer setting a[2] to 1 and everything else to 0. But with a[2] = 1 we have a non-designated initializer containing an assignment expression, and we fall down a rabbit hole.




    Here's what I've found so far:




    • a must be a local variable.




      6.7.8 Initialization



      1. All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.



      a[2] = 1 is not a constant expression, so a must have automatic storage.




    • a is in scope in its own initialization.




      6.2.1 Scopes of identifiers



      1. Structure, union, and enumeration tags have scope that begins just after the appearance of
        the tag in a type specifier that declares the tag. Each enumeration constant has scope that
        begins just after the appearance of its defining enumerator in an enumerator list. Any
        other identifier has scope that begins just after the completion of its declarator.




      The declarator is a[5], so variables are in scope in their own initialization.




    • a is alive in its own initialization.




      6.2.4 Storage durations of objects



      1. An object whose identifier is declared with no linkage and without the storage-class
        specifier static has automatic storage duration.


      2. For such an object that does not have a variable length array type, its lifetime extends
        from entry into the block with which it is associated until execution of that block ends
        in
        any way. (Entering an enclosed block or calling a function suspends, but does not end,
        execution of the current block.) If the block is entered recursively, a new instance of the
        object is created each time. The initial value of the object is indeterminate. If an
        initialization is specified for the object, it is performed each time the declaration is
        reached in the execution of the block; otherwise, the value becomes indeterminate each
        time the declaration is reached.





    • There is a sequence point after a[2]=1.




      6.8 Statements and blocks



      1. 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; the expression in an expression
        statement; the controlling expression of a selection statement (if or switch); the
        controlling expression of a while or do statement; each of the (optional) expressions of
        a for statement; the (optional) expression in a return statement. The end of a full
        expression is a sequence point.




      Note that e.g. in int foo = 1, 2, 3 the 1, 2, 3 part is a brace-enclosed list of initializers, each of which has a sequence point after it.




    • Initialization is performed in initializer list order.




      6.7.8 Initialization



      1. Each brace-enclosed initializer list has an associated current object. When no
        designations are present, subobjects of the current object are initialized in order according
        to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. [...]

       



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




    • However, initializer expressions are not necessarily evaluated in order.




      6.7.8 Initialization



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




    However, that still leaves some questions unanswered:




    • Are sequence points even relevant? The basic rule is:




      6.5 Expressions



      1. Between the previous and next sequence point an object shall have its stored value
        modified at most once by the evaluation of an expression. Furthermore, the prior value
        shall be read only to determine the value to be stored.



      a[2] = 1 is an expression, but initialization is not.



      This is slightly contradicted by Annex J:




      J.2 Undefined behavior



      • Between two sequence points, an object is modified more than once, or is modified
        and the prior value is read other than to determine the value to be stored (6.5).



      Annex J says any modification counts, not just modifications by expressions. But given that annexes are non-normative, we can probably ignore that.



    • How are the subobject initializations sequenced with respect to initializer expressions? Are all initializers evaluated first (in some order), then the subobjects are initialized with the results (in initializer list order)? Or can they be interleaved?



    I think int a[5] = a[2] = 1 is executed as follows:



    1. Storage for a is allocated when its containing block is entered. The contents are indeterminate at this point.

    2. The (only) initializer is executed (a[2] = 1), followed by a sequence point. This stores 1 in a[2] and returns 1.

    3. That 1 is used to initialize a[0] (the first initializer initializes the first subobject).

    But here things get fuzzy because the remaining elements (a[1], a[2], a[3], a[4]) are supposed to be initialized to 0, but it's not clear when: Does it happen before a[2] = 1 is evaluated? If so, a[2] = 1 would "win" and overwrite a[2], but would that assignment have undefined behavior because there is no sequence point between the zero initialization and the assignment expression? Are sequence points even relevant (see above)? Or does zero initialization happen after all initializers are evaluated? If so, a[2] should end up being 0.



    Because the C standard does not clearly define what happens here, I believe the behavior is undefined (by omission).






    share|improve this answer


















    • 1




      Instead of undefined I would argue that it's unspecified, which leave things open for interpretation by the implementations.
      – Some programmer dude
      7 hours ago










    • I don't think any of the stuff before your last horizontal rule is relevant to the question; the only issue is when the zero initialization happens (which the standard doesn't seem to say). The same issue would be raised by int a[5] = a[2] = a[3] ;
      – M.M
      7 hours ago











    • "we fall into a rabbit hole" LOL! Never heard that for an UB or unspecified stuff.
      – BЈовић
      6 hours ago










    • I think 6.7.8.19 can be read in two ways, both of which give "valid" outcomes, but both of which are undesirable readings. 1) We consider a[2] = 1 to initialize a[2] (even though it is explicitly not "an initializer" for a[2]) because it "sets an initial value" (as parenthetically mentioned in 5.1.2), which makes the outcome with a[2] == 1 well-defined (because it was "initialized explicitly" and so mustn't be set to 0). This is undesirable because it sneaks in initialization without an initializer, bypassing all the careful words about how the order of initializers is not specified...
      – Jeroen Mostert
      3 hours ago











    • ... or 2) we consider a[2] = 1 not to initialize by the narrow interpretation of "initialize" where an assignment does not "initialize", but then a[2] must be set to 0 by 6.7.8.19. This is undesirable because it clashes with the most obvious implementation of initialization (initialize the whole block to 0, then process initializers), puts an undue burden on the compiler, and makes the whole thing useless anyway. I would agree that by the way things are currently worded, the whole thing should probably be considered undefined by virtue of the standard not being explicit enough.
      – Jeroen Mostert
      3 hours ago













    up vote
    36
    down vote










    up vote
    36
    down vote









    TL;DR: I don't think the behavior of int a[5]=a[2]=1; is well defined, at least in C99.



    The funny part is that the only bit that makes sense to me is the part you're asking about: a[0] is set to 1 because the assignment operator returns the value that was assigned. It's everything else that's unclear.



    If the code had been int a[5] = [2] = 1 , everything would've been easy: That's a designated initializer setting a[2] to 1 and everything else to 0. But with a[2] = 1 we have a non-designated initializer containing an assignment expression, and we fall down a rabbit hole.




    Here's what I've found so far:




    • a must be a local variable.




      6.7.8 Initialization



      1. All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.



      a[2] = 1 is not a constant expression, so a must have automatic storage.




    • a is in scope in its own initialization.




      6.2.1 Scopes of identifiers



      1. Structure, union, and enumeration tags have scope that begins just after the appearance of
        the tag in a type specifier that declares the tag. Each enumeration constant has scope that
        begins just after the appearance of its defining enumerator in an enumerator list. Any
        other identifier has scope that begins just after the completion of its declarator.




      The declarator is a[5], so variables are in scope in their own initialization.




    • a is alive in its own initialization.




      6.2.4 Storage durations of objects



      1. An object whose identifier is declared with no linkage and without the storage-class
        specifier static has automatic storage duration.


      2. For such an object that does not have a variable length array type, its lifetime extends
        from entry into the block with which it is associated until execution of that block ends
        in
        any way. (Entering an enclosed block or calling a function suspends, but does not end,
        execution of the current block.) If the block is entered recursively, a new instance of the
        object is created each time. The initial value of the object is indeterminate. If an
        initialization is specified for the object, it is performed each time the declaration is
        reached in the execution of the block; otherwise, the value becomes indeterminate each
        time the declaration is reached.





    • There is a sequence point after a[2]=1.




      6.8 Statements and blocks



      1. 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; the expression in an expression
        statement; the controlling expression of a selection statement (if or switch); the
        controlling expression of a while or do statement; each of the (optional) expressions of
        a for statement; the (optional) expression in a return statement. The end of a full
        expression is a sequence point.




      Note that e.g. in int foo = 1, 2, 3 the 1, 2, 3 part is a brace-enclosed list of initializers, each of which has a sequence point after it.




    • Initialization is performed in initializer list order.




      6.7.8 Initialization



      1. Each brace-enclosed initializer list has an associated current object. When no
        designations are present, subobjects of the current object are initialized in order according
        to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. [...]

       



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




    • However, initializer expressions are not necessarily evaluated in order.




      6.7.8 Initialization



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




    However, that still leaves some questions unanswered:




    • Are sequence points even relevant? The basic rule is:




      6.5 Expressions



      1. Between the previous and next sequence point an object shall have its stored value
        modified at most once by the evaluation of an expression. Furthermore, the prior value
        shall be read only to determine the value to be stored.



      a[2] = 1 is an expression, but initialization is not.



      This is slightly contradicted by Annex J:




      J.2 Undefined behavior



      • Between two sequence points, an object is modified more than once, or is modified
        and the prior value is read other than to determine the value to be stored (6.5).



      Annex J says any modification counts, not just modifications by expressions. But given that annexes are non-normative, we can probably ignore that.



    • How are the subobject initializations sequenced with respect to initializer expressions? Are all initializers evaluated first (in some order), then the subobjects are initialized with the results (in initializer list order)? Or can they be interleaved?



    I think int a[5] = a[2] = 1 is executed as follows:



    1. Storage for a is allocated when its containing block is entered. The contents are indeterminate at this point.

    2. The (only) initializer is executed (a[2] = 1), followed by a sequence point. This stores 1 in a[2] and returns 1.

    3. That 1 is used to initialize a[0] (the first initializer initializes the first subobject).

    But here things get fuzzy because the remaining elements (a[1], a[2], a[3], a[4]) are supposed to be initialized to 0, but it's not clear when: Does it happen before a[2] = 1 is evaluated? If so, a[2] = 1 would "win" and overwrite a[2], but would that assignment have undefined behavior because there is no sequence point between the zero initialization and the assignment expression? Are sequence points even relevant (see above)? Or does zero initialization happen after all initializers are evaluated? If so, a[2] should end up being 0.



    Because the C standard does not clearly define what happens here, I believe the behavior is undefined (by omission).






    share|improve this answer














    TL;DR: I don't think the behavior of int a[5]=a[2]=1; is well defined, at least in C99.



    The funny part is that the only bit that makes sense to me is the part you're asking about: a[0] is set to 1 because the assignment operator returns the value that was assigned. It's everything else that's unclear.



    If the code had been int a[5] = [2] = 1 , everything would've been easy: That's a designated initializer setting a[2] to 1 and everything else to 0. But with a[2] = 1 we have a non-designated initializer containing an assignment expression, and we fall down a rabbit hole.




    Here's what I've found so far:




    • a must be a local variable.




      6.7.8 Initialization



      1. All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.



      a[2] = 1 is not a constant expression, so a must have automatic storage.




    • a is in scope in its own initialization.




      6.2.1 Scopes of identifiers



      1. Structure, union, and enumeration tags have scope that begins just after the appearance of
        the tag in a type specifier that declares the tag. Each enumeration constant has scope that
        begins just after the appearance of its defining enumerator in an enumerator list. Any
        other identifier has scope that begins just after the completion of its declarator.




      The declarator is a[5], so variables are in scope in their own initialization.




    • a is alive in its own initialization.




      6.2.4 Storage durations of objects



      1. An object whose identifier is declared with no linkage and without the storage-class
        specifier static has automatic storage duration.


      2. For such an object that does not have a variable length array type, its lifetime extends
        from entry into the block with which it is associated until execution of that block ends
        in
        any way. (Entering an enclosed block or calling a function suspends, but does not end,
        execution of the current block.) If the block is entered recursively, a new instance of the
        object is created each time. The initial value of the object is indeterminate. If an
        initialization is specified for the object, it is performed each time the declaration is
        reached in the execution of the block; otherwise, the value becomes indeterminate each
        time the declaration is reached.





    • There is a sequence point after a[2]=1.




      6.8 Statements and blocks



      1. 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; the expression in an expression
        statement; the controlling expression of a selection statement (if or switch); the
        controlling expression of a while or do statement; each of the (optional) expressions of
        a for statement; the (optional) expression in a return statement. The end of a full
        expression is a sequence point.




      Note that e.g. in int foo = 1, 2, 3 the 1, 2, 3 part is a brace-enclosed list of initializers, each of which has a sequence point after it.




    • Initialization is performed in initializer list order.




      6.7.8 Initialization



      1. Each brace-enclosed initializer list has an associated current object. When no
        designations are present, subobjects of the current object are initialized in order according
        to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. [...]

       



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




    • However, initializer expressions are not necessarily evaluated in order.




      6.7.8 Initialization



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




    However, that still leaves some questions unanswered:




    • Are sequence points even relevant? The basic rule is:




      6.5 Expressions



      1. Between the previous and next sequence point an object shall have its stored value
        modified at most once by the evaluation of an expression. Furthermore, the prior value
        shall be read only to determine the value to be stored.



      a[2] = 1 is an expression, but initialization is not.



      This is slightly contradicted by Annex J:




      J.2 Undefined behavior



      • Between two sequence points, an object is modified more than once, or is modified
        and the prior value is read other than to determine the value to be stored (6.5).



      Annex J says any modification counts, not just modifications by expressions. But given that annexes are non-normative, we can probably ignore that.



    • How are the subobject initializations sequenced with respect to initializer expressions? Are all initializers evaluated first (in some order), then the subobjects are initialized with the results (in initializer list order)? Or can they be interleaved?



    I think int a[5] = a[2] = 1 is executed as follows:



    1. Storage for a is allocated when its containing block is entered. The contents are indeterminate at this point.

    2. The (only) initializer is executed (a[2] = 1), followed by a sequence point. This stores 1 in a[2] and returns 1.

    3. That 1 is used to initialize a[0] (the first initializer initializes the first subobject).

    But here things get fuzzy because the remaining elements (a[1], a[2], a[3], a[4]) are supposed to be initialized to 0, but it's not clear when: Does it happen before a[2] = 1 is evaluated? If so, a[2] = 1 would "win" and overwrite a[2], but would that assignment have undefined behavior because there is no sequence point between the zero initialization and the assignment expression? Are sequence points even relevant (see above)? Or does zero initialization happen after all initializers are evaluated? If so, a[2] should end up being 0.



    Because the C standard does not clearly define what happens here, I believe the behavior is undefined (by omission).







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 8 mins ago

























    answered 8 hours ago









    melpomene

    50.4k53682




    50.4k53682







    • 1




      Instead of undefined I would argue that it's unspecified, which leave things open for interpretation by the implementations.
      – Some programmer dude
      7 hours ago










    • I don't think any of the stuff before your last horizontal rule is relevant to the question; the only issue is when the zero initialization happens (which the standard doesn't seem to say). The same issue would be raised by int a[5] = a[2] = a[3] ;
      – M.M
      7 hours ago











    • "we fall into a rabbit hole" LOL! Never heard that for an UB or unspecified stuff.
      – BЈовић
      6 hours ago










    • I think 6.7.8.19 can be read in two ways, both of which give "valid" outcomes, but both of which are undesirable readings. 1) We consider a[2] = 1 to initialize a[2] (even though it is explicitly not "an initializer" for a[2]) because it "sets an initial value" (as parenthetically mentioned in 5.1.2), which makes the outcome with a[2] == 1 well-defined (because it was "initialized explicitly" and so mustn't be set to 0). This is undesirable because it sneaks in initialization without an initializer, bypassing all the careful words about how the order of initializers is not specified...
      – Jeroen Mostert
      3 hours ago











    • ... or 2) we consider a[2] = 1 not to initialize by the narrow interpretation of "initialize" where an assignment does not "initialize", but then a[2] must be set to 0 by 6.7.8.19. This is undesirable because it clashes with the most obvious implementation of initialization (initialize the whole block to 0, then process initializers), puts an undue burden on the compiler, and makes the whole thing useless anyway. I would agree that by the way things are currently worded, the whole thing should probably be considered undefined by virtue of the standard not being explicit enough.
      – Jeroen Mostert
      3 hours ago













    • 1




      Instead of undefined I would argue that it's unspecified, which leave things open for interpretation by the implementations.
      – Some programmer dude
      7 hours ago










    • I don't think any of the stuff before your last horizontal rule is relevant to the question; the only issue is when the zero initialization happens (which the standard doesn't seem to say). The same issue would be raised by int a[5] = a[2] = a[3] ;
      – M.M
      7 hours ago











    • "we fall into a rabbit hole" LOL! Never heard that for an UB or unspecified stuff.
      – BЈовић
      6 hours ago










    • I think 6.7.8.19 can be read in two ways, both of which give "valid" outcomes, but both of which are undesirable readings. 1) We consider a[2] = 1 to initialize a[2] (even though it is explicitly not "an initializer" for a[2]) because it "sets an initial value" (as parenthetically mentioned in 5.1.2), which makes the outcome with a[2] == 1 well-defined (because it was "initialized explicitly" and so mustn't be set to 0). This is undesirable because it sneaks in initialization without an initializer, bypassing all the careful words about how the order of initializers is not specified...
      – Jeroen Mostert
      3 hours ago











    • ... or 2) we consider a[2] = 1 not to initialize by the narrow interpretation of "initialize" where an assignment does not "initialize", but then a[2] must be set to 0 by 6.7.8.19. This is undesirable because it clashes with the most obvious implementation of initialization (initialize the whole block to 0, then process initializers), puts an undue burden on the compiler, and makes the whole thing useless anyway. I would agree that by the way things are currently worded, the whole thing should probably be considered undefined by virtue of the standard not being explicit enough.
      – Jeroen Mostert
      3 hours ago








    1




    1




    Instead of undefined I would argue that it's unspecified, which leave things open for interpretation by the implementations.
    – Some programmer dude
    7 hours ago




    Instead of undefined I would argue that it's unspecified, which leave things open for interpretation by the implementations.
    – Some programmer dude
    7 hours ago












    I don't think any of the stuff before your last horizontal rule is relevant to the question; the only issue is when the zero initialization happens (which the standard doesn't seem to say). The same issue would be raised by int a[5] = a[2] = a[3] ;
    – M.M
    7 hours ago





    I don't think any of the stuff before your last horizontal rule is relevant to the question; the only issue is when the zero initialization happens (which the standard doesn't seem to say). The same issue would be raised by int a[5] = a[2] = a[3] ;
    – M.M
    7 hours ago













    "we fall into a rabbit hole" LOL! Never heard that for an UB or unspecified stuff.
    – BЈовић
    6 hours ago




    "we fall into a rabbit hole" LOL! Never heard that for an UB or unspecified stuff.
    – BЈовић
    6 hours ago












    I think 6.7.8.19 can be read in two ways, both of which give "valid" outcomes, but both of which are undesirable readings. 1) We consider a[2] = 1 to initialize a[2] (even though it is explicitly not "an initializer" for a[2]) because it "sets an initial value" (as parenthetically mentioned in 5.1.2), which makes the outcome with a[2] == 1 well-defined (because it was "initialized explicitly" and so mustn't be set to 0). This is undesirable because it sneaks in initialization without an initializer, bypassing all the careful words about how the order of initializers is not specified...
    – Jeroen Mostert
    3 hours ago





    I think 6.7.8.19 can be read in two ways, both of which give "valid" outcomes, but both of which are undesirable readings. 1) We consider a[2] = 1 to initialize a[2] (even though it is explicitly not "an initializer" for a[2]) because it "sets an initial value" (as parenthetically mentioned in 5.1.2), which makes the outcome with a[2] == 1 well-defined (because it was "initialized explicitly" and so mustn't be set to 0). This is undesirable because it sneaks in initialization without an initializer, bypassing all the careful words about how the order of initializers is not specified...
    – Jeroen Mostert
    3 hours ago













    ... or 2) we consider a[2] = 1 not to initialize by the narrow interpretation of "initialize" where an assignment does not "initialize", but then a[2] must be set to 0 by 6.7.8.19. This is undesirable because it clashes with the most obvious implementation of initialization (initialize the whole block to 0, then process initializers), puts an undue burden on the compiler, and makes the whole thing useless anyway. I would agree that by the way things are currently worded, the whole thing should probably be considered undefined by virtue of the standard not being explicit enough.
    – Jeroen Mostert
    3 hours ago





    ... or 2) we consider a[2] = 1 not to initialize by the narrow interpretation of "initialize" where an assignment does not "initialize", but then a[2] must be set to 0 by 6.7.8.19. This is undesirable because it clashes with the most obvious implementation of initialization (initialize the whole block to 0, then process initializers), puts an undue burden on the compiler, and makes the whole thing useless anyway. I would agree that by the way things are currently worded, the whole thing should probably be considered undefined by virtue of the standard not being explicit enough.
    – Jeroen Mostert
    3 hours ago













    up vote
    14
    down vote














    I don't understand, why does a[0] print 1 instead of 0?




    Presumably a[2]=1 initializes a[2] first, and the result of the expression is used to initialize a[0].



    From N2176 (C17 draft):




    6.7.9 Initialization



    1. 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. 154)



    So it would seem that output 1 0 0 0 0 would also have been possible.



    Conclusion: Don't write initializers that modifies the initialized variable on the fly.






    share|improve this answer


















    • 1




      That part does not apply: There is only one initializer expression here, so it doesn't need to be sequenced with anything.
      – melpomene
      8 hours ago










    • @melpomene There is the ... expression which initializes a[2] to 0, and a[2]=1 sub-expression which initializes a[2] to 1.
      – user694733
      8 hours ago






    • 1




      ... is a braced initializer list. It is not an expression.
      – melpomene
      8 hours ago










    • @melpomene Ok, you may be right there. But I would still argue there are still 2 competing side-effects so that paragraph stands.
      – user694733
      8 hours ago










    • @Gerhardh Yeah, I started having doubts about that too. I removed it. Thanks.
      – user694733
      7 hours ago














    up vote
    14
    down vote














    I don't understand, why does a[0] print 1 instead of 0?




    Presumably a[2]=1 initializes a[2] first, and the result of the expression is used to initialize a[0].



    From N2176 (C17 draft):




    6.7.9 Initialization



    1. 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. 154)



    So it would seem that output 1 0 0 0 0 would also have been possible.



    Conclusion: Don't write initializers that modifies the initialized variable on the fly.






    share|improve this answer


















    • 1




      That part does not apply: There is only one initializer expression here, so it doesn't need to be sequenced with anything.
      – melpomene
      8 hours ago










    • @melpomene There is the ... expression which initializes a[2] to 0, and a[2]=1 sub-expression which initializes a[2] to 1.
      – user694733
      8 hours ago






    • 1




      ... is a braced initializer list. It is not an expression.
      – melpomene
      8 hours ago










    • @melpomene Ok, you may be right there. But I would still argue there are still 2 competing side-effects so that paragraph stands.
      – user694733
      8 hours ago










    • @Gerhardh Yeah, I started having doubts about that too. I removed it. Thanks.
      – user694733
      7 hours ago












    up vote
    14
    down vote










    up vote
    14
    down vote










    I don't understand, why does a[0] print 1 instead of 0?




    Presumably a[2]=1 initializes a[2] first, and the result of the expression is used to initialize a[0].



    From N2176 (C17 draft):




    6.7.9 Initialization



    1. 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. 154)



    So it would seem that output 1 0 0 0 0 would also have been possible.



    Conclusion: Don't write initializers that modifies the initialized variable on the fly.






    share|improve this answer















    I don't understand, why does a[0] print 1 instead of 0?




    Presumably a[2]=1 initializes a[2] first, and the result of the expression is used to initialize a[0].



    From N2176 (C17 draft):




    6.7.9 Initialization



    1. 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. 154)



    So it would seem that output 1 0 0 0 0 would also have been possible.



    Conclusion: Don't write initializers that modifies the initialized variable on the fly.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 7 hours ago

























    answered 8 hours ago









    user694733

    10.3k12749




    10.3k12749







    • 1




      That part does not apply: There is only one initializer expression here, so it doesn't need to be sequenced with anything.
      – melpomene
      8 hours ago










    • @melpomene There is the ... expression which initializes a[2] to 0, and a[2]=1 sub-expression which initializes a[2] to 1.
      – user694733
      8 hours ago






    • 1




      ... is a braced initializer list. It is not an expression.
      – melpomene
      8 hours ago










    • @melpomene Ok, you may be right there. But I would still argue there are still 2 competing side-effects so that paragraph stands.
      – user694733
      8 hours ago










    • @Gerhardh Yeah, I started having doubts about that too. I removed it. Thanks.
      – user694733
      7 hours ago












    • 1




      That part does not apply: There is only one initializer expression here, so it doesn't need to be sequenced with anything.
      – melpomene
      8 hours ago










    • @melpomene There is the ... expression which initializes a[2] to 0, and a[2]=1 sub-expression which initializes a[2] to 1.
      – user694733
      8 hours ago






    • 1




      ... is a braced initializer list. It is not an expression.
      – melpomene
      8 hours ago










    • @melpomene Ok, you may be right there. But I would still argue there are still 2 competing side-effects so that paragraph stands.
      – user694733
      8 hours ago










    • @Gerhardh Yeah, I started having doubts about that too. I removed it. Thanks.
      – user694733
      7 hours ago







    1




    1




    That part does not apply: There is only one initializer expression here, so it doesn't need to be sequenced with anything.
    – melpomene
    8 hours ago




    That part does not apply: There is only one initializer expression here, so it doesn't need to be sequenced with anything.
    – melpomene
    8 hours ago












    @melpomene There is the ... expression which initializes a[2] to 0, and a[2]=1 sub-expression which initializes a[2] to 1.
    – user694733
    8 hours ago




    @melpomene There is the ... expression which initializes a[2] to 0, and a[2]=1 sub-expression which initializes a[2] to 1.
    – user694733
    8 hours ago




    1




    1




    ... is a braced initializer list. It is not an expression.
    – melpomene
    8 hours ago




    ... is a braced initializer list. It is not an expression.
    – melpomene
    8 hours ago












    @melpomene Ok, you may be right there. But I would still argue there are still 2 competing side-effects so that paragraph stands.
    – user694733
    8 hours ago




    @melpomene Ok, you may be right there. But I would still argue there are still 2 competing side-effects so that paragraph stands.
    – user694733
    8 hours ago












    @Gerhardh Yeah, I started having doubts about that too. I removed it. Thanks.
    – user694733
    7 hours ago




    @Gerhardh Yeah, I started having doubts about that too. I removed it. Thanks.
    – user694733
    7 hours ago










    up vote
    1
    down vote













    My Understanding is
    a[2]=1 returns value 1 so code becomes



    int a[5]=a[2]=1 --> int a[5]=1


    int a[5]=1 assign value for a[0]=1



    Hence it print 1 for a[0]



    For example



    char str[10]=‘H’,‘a’,‘i’;


    char str[0] = ‘H’;
    char str[1] = ‘a’;
    char str[2] = ‘i;





    share|improve this answer
























      up vote
      1
      down vote













      My Understanding is
      a[2]=1 returns value 1 so code becomes



      int a[5]=a[2]=1 --> int a[5]=1


      int a[5]=1 assign value for a[0]=1



      Hence it print 1 for a[0]



      For example



      char str[10]=‘H’,‘a’,‘i’;


      char str[0] = ‘H’;
      char str[1] = ‘a’;
      char str[2] = ‘i;





      share|improve this answer






















        up vote
        1
        down vote










        up vote
        1
        down vote









        My Understanding is
        a[2]=1 returns value 1 so code becomes



        int a[5]=a[2]=1 --> int a[5]=1


        int a[5]=1 assign value for a[0]=1



        Hence it print 1 for a[0]



        For example



        char str[10]=‘H’,‘a’,‘i’;


        char str[0] = ‘H’;
        char str[1] = ‘a’;
        char str[2] = ‘i;





        share|improve this answer












        My Understanding is
        a[2]=1 returns value 1 so code becomes



        int a[5]=a[2]=1 --> int a[5]=1


        int a[5]=1 assign value for a[0]=1



        Hence it print 1 for a[0]



        For example



        char str[10]=‘H’,‘a’,‘i’;


        char str[0] = ‘H’;
        char str[1] = ‘a’;
        char str[2] = ‘i;






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 6 hours ago









        Karthika Kavi

        425




        425




















            up vote
            1
            down vote













            I try to give a short and simple answer to the puzzle: int a[5] = a[2] = 1 ;



            1. First a[2] = 1 is set. That means the array says: 0 0 1 0 0

            2. But behold, given that you did it in the brackets, which are used to initialize the array in order, it takes the first value (which is 1) and sets that to a[0]. It is as if int a[5] = a[2] ; would remain, where we already got a[2] = 2. The resulting array is now: 1 0 1 0 0

            I guess it has to do with declarations being done first (or even the last declaration being done first). Example: A = B = C = 5, it starts with C = 5, goes to B = C, then A = B.



            That raises the question how a[2] can be declared if the array should not exist yet. Well, it actually does the moment ... is read, which is logical, given that every value in that bracket must be assigned to an already allocated array anyway. It's special alias/syntax for array initiation.






            share|improve this answer




















            • Small info: Another example: int a[6] = a[3] = 1, a[4] = 2, a[5] = 3 ; - It goes from left to right, each starting with the internal declaration. It goes in 6 steps: 0 0 0 1 0 0, 1 0 0 1 0 0, 1 0 0 1 2 0, 1 2 0 1 2 0, 1 2 0 1 2 3, 1 2 3 1 2 3.
              – Battle
              4 hours ago











            • A = B = C = 5 is not a declaration (or initialization). It's a normal expression that parses as A = (B = (C = 5)) because the = operator is right associative. That doesn't really help with explaining how initialization works. The array actually starts existing when the block it is defined in is entered, which can be long before the actual definition is executed.
              – melpomene
              17 mins ago














            up vote
            1
            down vote













            I try to give a short and simple answer to the puzzle: int a[5] = a[2] = 1 ;



            1. First a[2] = 1 is set. That means the array says: 0 0 1 0 0

            2. But behold, given that you did it in the brackets, which are used to initialize the array in order, it takes the first value (which is 1) and sets that to a[0]. It is as if int a[5] = a[2] ; would remain, where we already got a[2] = 2. The resulting array is now: 1 0 1 0 0

            I guess it has to do with declarations being done first (or even the last declaration being done first). Example: A = B = C = 5, it starts with C = 5, goes to B = C, then A = B.



            That raises the question how a[2] can be declared if the array should not exist yet. Well, it actually does the moment ... is read, which is logical, given that every value in that bracket must be assigned to an already allocated array anyway. It's special alias/syntax for array initiation.






            share|improve this answer




















            • Small info: Another example: int a[6] = a[3] = 1, a[4] = 2, a[5] = 3 ; - It goes from left to right, each starting with the internal declaration. It goes in 6 steps: 0 0 0 1 0 0, 1 0 0 1 0 0, 1 0 0 1 2 0, 1 2 0 1 2 0, 1 2 0 1 2 3, 1 2 3 1 2 3.
              – Battle
              4 hours ago











            • A = B = C = 5 is not a declaration (or initialization). It's a normal expression that parses as A = (B = (C = 5)) because the = operator is right associative. That doesn't really help with explaining how initialization works. The array actually starts existing when the block it is defined in is entered, which can be long before the actual definition is executed.
              – melpomene
              17 mins ago












            up vote
            1
            down vote










            up vote
            1
            down vote









            I try to give a short and simple answer to the puzzle: int a[5] = a[2] = 1 ;



            1. First a[2] = 1 is set. That means the array says: 0 0 1 0 0

            2. But behold, given that you did it in the brackets, which are used to initialize the array in order, it takes the first value (which is 1) and sets that to a[0]. It is as if int a[5] = a[2] ; would remain, where we already got a[2] = 2. The resulting array is now: 1 0 1 0 0

            I guess it has to do with declarations being done first (or even the last declaration being done first). Example: A = B = C = 5, it starts with C = 5, goes to B = C, then A = B.



            That raises the question how a[2] can be declared if the array should not exist yet. Well, it actually does the moment ... is read, which is logical, given that every value in that bracket must be assigned to an already allocated array anyway. It's special alias/syntax for array initiation.






            share|improve this answer












            I try to give a short and simple answer to the puzzle: int a[5] = a[2] = 1 ;



            1. First a[2] = 1 is set. That means the array says: 0 0 1 0 0

            2. But behold, given that you did it in the brackets, which are used to initialize the array in order, it takes the first value (which is 1) and sets that to a[0]. It is as if int a[5] = a[2] ; would remain, where we already got a[2] = 2. The resulting array is now: 1 0 1 0 0

            I guess it has to do with declarations being done first (or even the last declaration being done first). Example: A = B = C = 5, it starts with C = 5, goes to B = C, then A = B.



            That raises the question how a[2] can be declared if the array should not exist yet. Well, it actually does the moment ... is read, which is logical, given that every value in that bracket must be assigned to an already allocated array anyway. It's special alias/syntax for array initiation.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered 4 hours ago









            Battle

            27817




            27817











            • Small info: Another example: int a[6] = a[3] = 1, a[4] = 2, a[5] = 3 ; - It goes from left to right, each starting with the internal declaration. It goes in 6 steps: 0 0 0 1 0 0, 1 0 0 1 0 0, 1 0 0 1 2 0, 1 2 0 1 2 0, 1 2 0 1 2 3, 1 2 3 1 2 3.
              – Battle
              4 hours ago











            • A = B = C = 5 is not a declaration (or initialization). It's a normal expression that parses as A = (B = (C = 5)) because the = operator is right associative. That doesn't really help with explaining how initialization works. The array actually starts existing when the block it is defined in is entered, which can be long before the actual definition is executed.
              – melpomene
              17 mins ago
















            • Small info: Another example: int a[6] = a[3] = 1, a[4] = 2, a[5] = 3 ; - It goes from left to right, each starting with the internal declaration. It goes in 6 steps: 0 0 0 1 0 0, 1 0 0 1 0 0, 1 0 0 1 2 0, 1 2 0 1 2 0, 1 2 0 1 2 3, 1 2 3 1 2 3.
              – Battle
              4 hours ago











            • A = B = C = 5 is not a declaration (or initialization). It's a normal expression that parses as A = (B = (C = 5)) because the = operator is right associative. That doesn't really help with explaining how initialization works. The array actually starts existing when the block it is defined in is entered, which can be long before the actual definition is executed.
              – melpomene
              17 mins ago















            Small info: Another example: int a[6] = a[3] = 1, a[4] = 2, a[5] = 3 ; - It goes from left to right, each starting with the internal declaration. It goes in 6 steps: 0 0 0 1 0 0, 1 0 0 1 0 0, 1 0 0 1 2 0, 1 2 0 1 2 0, 1 2 0 1 2 3, 1 2 3 1 2 3.
            – Battle
            4 hours ago





            Small info: Another example: int a[6] = a[3] = 1, a[4] = 2, a[5] = 3 ; - It goes from left to right, each starting with the internal declaration. It goes in 6 steps: 0 0 0 1 0 0, 1 0 0 1 0 0, 1 0 0 1 2 0, 1 2 0 1 2 0, 1 2 0 1 2 3, 1 2 3 1 2 3.
            – Battle
            4 hours ago













            A = B = C = 5 is not a declaration (or initialization). It's a normal expression that parses as A = (B = (C = 5)) because the = operator is right associative. That doesn't really help with explaining how initialization works. The array actually starts existing when the block it is defined in is entered, which can be long before the actual definition is executed.
            – melpomene
            17 mins ago




            A = B = C = 5 is not a declaration (or initialization). It's a normal expression that parses as A = (B = (C = 5)) because the = operator is right associative. That doesn't really help with explaining how initialization works. The array actually starts existing when the block it is defined in is entered, which can be long before the actual definition is executed.
            – melpomene
            17 mins ago

















             

            draft saved


            draft discarded















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52307474%2fconfusion-about-array-initialization-in-c%23new-answer', 'question_page');

            );

            Post as a guest













































































            Comments

            Popular posts from this blog

            What does second last employer means? [closed]

            List of Gilmore Girls characters

            Confectionery