Is there literally any difference between String and Null-terminated character array in C

Viewed 133

Wanted to ask are strings are null-terminated character arrays interchangeable in c. Like

    char string3[] = "abc";
    char string4[4] = {'a','b','c','\0'};

    if(!strcmp(string3,string4)){
        printf("yes");
    }

Even the strcmp also gives Yes. So, there is only the difference in initialization or is there some difference deep down there.

2 Answers
char string3[] = "abc";

is exactly short for

char string4[4] = {'a','b','c','\0'};

and there is no difference.

However, note that

//  now it's a pointer
//   |
//   v
char *string3b = "abc";

and

char string4[4] = {'a','b','c','\0'};

are not equivalent. Whenever you write a string and don't initialize an array with it (pointers are not arrays), the compiler creates an array for you. The first one is basically equivalent to:

static const char _magic_compiler_variable_for_abc_string[4] = {'a','b','c','\0'};
char *string3b = (char*)_magic_compiler_variable_for_abc_string;

so the string is allocated somewhere else and string3b only holds a pointer to it. Because the string variable is const (even though the pointer is not const), you are not allowed to edit the string in this case. The compiler may use the same _magic_compiler_variable_for_abc_string everywhere you write "abc", or it may create separate ones.

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.

Related