The actual issue here is whether the function is passed a pointer to data that may be changed or not, not whether the function is passed a string or not.
Per C 2018 7.1.1 1, “A string is a contiguous sequence of characters terminated by and including the first null character.” So, with char a[] = "HELLO";
, the array contains a string.
With char *b = "HELLO";
, b
points to the first character of a string literal1. The data in a string literal ought to be treated as constant—a program should not attempt to modify it, and the C implementation may put it in read-only memory so that attempting to modify it results in a program exception.
If we were designing the C language from scratch, string literals would be qualified with const
. However, const
did not exist when string literals were created. Programmers simply had to know when data was part of a string literal, and it was, and still is, their responsibility to avoid attempting to change it.
To some extent, you can ameliorate this issue by adding const
manually, as with const char *b = "HELLO";
. Then attempting to pass b
to func
will result in a compiler diagnostic message about attempting to pass const char *
argument for a char *
parameter.
If it were deemed sufficiently important to guard against bugs, all string literals could be written as compound literals, such as (const char []) { "HELLO" }
, and a macro could be defined to do this:
#define StringLiteral(x) ((const char []) { (x) })
Footnote
1 Technically, a string literal is the characters in the source code, such as "HELLO"
, including the quotation marks. When a program is compiled, a string literal in source code results in creation of an array containing a string. Informally, we speak of this array as a string literal.