char string3[] = "abc";
char string4[] = {'a','b','c','\0'};
The main difference is that the string literal initializer consists of a sequence of multi-byte characters, whereas the array initializer list consists of a sequence of integer constant expressions each of which is formed from a multi-byte character constant. If any of the characters were not representable by a single char, their contents would be different. string3[] would be longer than 4 bytes and string4[] would be exactly 4 bytes long but with the values of some of its elements truncated. It shouldn't affect the characters a, b, c which are part of the basic character set and should therefore fit in a single char.
For example, on my system where C uses UTF-8 as the source and execution character set1, the following program:
#include <stdio.h>
int main(void)
{
char string1[] = "αβγ";
char string2[] = { 'α', 'β', 'γ', '\0' };
printf("sizeof string1 = %zu, sizeof string2 = %zu\n",
sizeof string1, sizeof string2);
return 0;
}
compiles (with warnings2) and produces the output:
sizeof string1 = 7, sizeof string2 = 4
The warnings from gcc 10 were:
foo.c: In function ‘main’:
foo.c:6:21: warning: multi-character character constant [-Wmultichar]
6 | char string2[] = { 'α', 'β', 'γ', '\0' };
| ^~~
foo.c:6:21: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘52913’ to ‘-79’ [-Woverflow]
foo.c:6:27: warning: multi-character character constant [-Wmultichar]
6 | char string2[] = { 'α', 'β', 'γ', '\0' };
| ^~~
foo.c:6:27: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘52914’ to ‘-78’ [-Woverflow]
foo.c:6:33: warning: multi-character character constant [-Wmultichar]
6 | char string2[] = { 'α', 'β', 'γ', '\0' };
| ^~~
foo.c:6:33: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘52915’ to ‘-77’ [-Woverflow]
If the execution character set is assumed to be UTF-81, the string literal intializer "αβγ" could be changed to an array initializer list by expanding out each character as its sequence of bytes:
char string5[] = { '\xce', '\xb1', '\xce', '\xb2', '\xce', '\xb3', '\0' };
If the array initializer needs to be a UTF-8 sequence regardless of the execution character set, it would be more readable to use an explicit u8-prefixed string literal initializer:
char string6[] = u8"αβγ";
The initial contents of string5[] and string6[] are the same regardless of execution character set, and will be the same as the initial contents of string1[] if and only if the execution character set is UTF-8.
1 By "character set" I really mean a character set plus an encoding, with the character set inferred from the specified encoding. I.e. by "UTF-8" I mean the UCS character set plus the UTF-8 transfer encoding.
2 Compilers are not required to issue any diagnostic message when compiling the given example, but some choose to do so.