1. Scalar: an expression, optionally enclosed in braces. int a = 1; // valid int a = { 1 }; // valid int a[] = { { 1 }, 2 }; // valid int a = {{ 1 }}; // warning in gcc, a == 1; error in MSVC int a = { { 1 }, 2 }; // warning in gcc, a == 1; error in MSVC int a = { 1, 2 }; // warning in gcc, a == 1; error in MSVC I'm following MSVC: you either put an expression, or add a single layer of brace. 2. Union: union A { int a; int b; }; union A u = { 1 }; // always initialize the first member, i.e. a, not b. union A u = {{ 1 }}; // valid union A u = another_union; // valid 3. Struct: struct A { int a; int b; }; struct A = another_struct; // valid struct A = { another_struct }; // error, once you put a brace, the compiler assumes you want to initialize members. From 2 and 3, once seen union or struct, either read expression or brace. 4. Array of characters: char a[] = { 'a', 'b' }; // valid char a[] = "abc"; // becomes char a[4]: include '\0' char a[3] = "abc"; // valid, ignore '\0' char a[2] = "abc"; // warning in gcc; error in MSVC If the aggregate contains members that are aggregates or unions, or if the first member of a union is an aggregate or union, the rules apply recursively to the subaggregates or contained unions. If the initializer of a subaggregate or contained union begins with a left brace, the initializers enclosed by that brace and its matching right brace initialize the members of the subaggregate or the first member of the contained union. Otherwise, only enough initializers from the list are taken to account for the members of the first subaggregate or the first member of the contained union; any remaining initializers are left to initialize the next member of the aggregate of which the current subaggregate or contained union is a part.