There are several common misconceptions surrounding the concept of strings in C, such as thinking that a string is a char array or a char pointer. Strings are commonly stored in char arrays, but there are char arrays that do not contain strings and strings that are not within char arrays. It is up to the programmer to ensure that when they pass a char pointer to a function that works on strings they’re really passing a pointer to string. There is no specific type for strings. You can only say that a particular object contains a string.

The C Programming Language as described in the C standards consists of both the language itself and the “C standard library”. Although strings are mentioned beforehand, the library is described in section 7, and it is there where strings are formally defined1:

A string is a contiguous sequence of characters terminated by and including the first null character. The term multibyte string is sometimes used instead to emphasize special processing given to multibyte characters contained in the string or to avoid confusion with a wide string. A pointer to a string is a pointer to its initial (lowest addressed) character. The length of a string is the number of bytes preceding the null character and the value of a string is the sequence of the values of the contained characters, in order.

The first sentence completely defines strings. The rest defines other terms associated with strings. Important concepts:

  • Strings are zero or more characters followed by a null terminator.
  • The length of a string doesn’t include the null. i.e. strlen(“hello”) is 5.

The following examples are of things that do not contain strings:

// These aren't strings because they
// have no null terminator.
char *a = (char[2]){ 1, 2 }; // doesn't point to a string.
char b[3] = "foo";
char c = 42;
    
// These examples are uninitialized and
// have indeterminate content. Assume they're
// in block scope.
char d[3];        
char *e;

The following examples contain strings:

char *p = "foo";  // p points to a string.
char x = 0;       // x contains a zero length string!
char y[] = "foo"; // y contains a string.
    
char *z = malloc(6); // this allocated storage will
                     // soon contain a string.
if (z) 
    strcpy(z, "hello"); // z points to a string.

Another concept of note is that y above actually contains four strings: “foo” starting at y + 0, “oo” starting at y + 1, “o” starting at y + 2 and “” starting (and ending) at y + 3.

You can therefore pass any of these, implicitly converting the arrays to pointers to their first element where appropriate, to functions expecting pointers to strings. However, if the function will attempt to modify the string, passing p there is invalid; it is undefined behavior to attempt to modify string literals2. Perhaps surprisingly, string literals are non-const arrays3, so a compiler needn’t stop you from trying to directly modify them.

References

  1. Definition of string — C11 §7.1.1p1

  2. Modifying string literals undefined — C11 §6.4.5p7

  3. String literals are non-const arrays — C11 §6.4.5p6