0

I'm looking for where const-promotion is defined in c/c++. It's an implicit conversion, but I cannot find any documentation on it.

This works on g++ while using the --pedantic flag

// prototypes for example
void foo(char*);
void bar(const char*);

char buffer[8];
snprintf(buffer,sizeof(buffer), "hello");

// note: string literals are of type const char*

foo(buffer);
foo("hello"); // works, but why
bar(buffer);
bar("hello");

The behavior represented above is the expected behavior. However I am looking for the documentation for this behavior. I have looked at (drafts of) the c++98 standard and stack overflow searching for "promotion" and "implicit conversion" and have not found an answer.

If this question is too broad, I am using C++98, so we can address it for that standard.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
kill -9
  • 159
  • 1
  • 9
  • 5
    C and C++ are two *very **different*** languages. The standards are very different, and even in sections that seem similar there might be differences in wording. Sections are also not ordered similarly in the two standards. So please pick *one* language, or this will be to broad. – Some programmer dude Jun 06 '19 at 22:25
  • 2
    You'd have better luck looking for "string literal" because it's an exception to the const rules. – chris Jun 06 '19 at 22:27
  • 1
    Also please try to create a [mcve] to show us, something which replicates the problem you have and which we can copy and try for ourselves. Also include information about your compiler, like its version, and how you run it (show the command). Lastly please take some time to refresh about [how to ask good questions](http://stackoverflow.com/help/how-to-ask), as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Jun 06 '19 at 22:27
  • 5
    "*string literals are of type const char\**" - That's not true for either C or C++. – melpomene Jun 06 '19 at 22:29
  • There is no const-promotion. As you say, there is a const **conversion**. – Pete Becker Jun 06 '19 at 23:29
  • 5
    @Someprogrammerdude: Please do not ask for reproducible examples when it is not appropriate. There is no “problem” to replicate. This question does not ask about code that is not working. The OP knows the expected behavior and knows how to write working code. The question they are asking is “However I am looking for the documentation for this behavior.” All the information needed for their question is already in the question, except it ought to be separated for C and C++. – Eric Postpischil Jun 06 '19 at 23:48
  • 1
    I have answered this for C. I suggest you repost with just a C++ tag and a language-lawyer tag. – Eric Postpischil Jun 06 '19 at 23:57

1 Answers1

3

This answer is for C and not C++.

Character string literals (distinguished from UTF-8 string literals or wide string literals) are arrays of char, per C 2018 6.4.5 61. For historical reasons, they are not arrays of const char, but they should be treated as const by programmers as, if a program tries to write to a string literal, the behavior is not defined by the C standard.

As an array, a string literal is automatically converted to a char * pointing to its first element, unless it is the operand of sizeof or unary & or is used to initialize an array.

Thus, in both foo(buffer) and foo("hello"), we have a char * argument passed to a char * parameter, and no conversion is necessary.

In bar(buffer) and bar("hello"), we have a char * argument passed to a const * parameter. The explanation for this follows.

For function calls where a prototype is visible, the arguments are converted to the types of the parameters as if by assignment, per C 2018 6.5.2.2 7:

If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type.…

(Note that the “unqualified version of its declared type” means a const int or char * const parameter would be int or char *, respectively, not that a const char * parameter would be char *.)

6.5.16.1 2 says:

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression…

The type of the assignment expression is that of the left operand, 6.5.16 3:

… The type of an assignment expression is the type the left operand would have after lvalue conversion.…

So now we know the char * is converted to const char *. This also satisfies the constraints for assignment in 6.5.16.1 1:

One of the following shall hold: … the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;…

And the pointer conversion is specified in 6.3.2.3 2:

For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.

For the snprintf call, the argument "hello" is passed in a location corresponding to ... in the parameters. For this, we look to the rest of 6.5.2.2 7, which continues from the first part quoted above:

… The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

The default argument promotions are in 6.5.2.2 6:

… the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

Those promotions do not affect pointers, so the pointer is passed with its type unchanged. That is interesting because we could pass either a char * or a const char * here. The specification for snprintf refers to fprintf, which, for %s specification, says in 7.21.6.1 8:

… the argument shall be a pointer to the initial element of an array of character type.…

So it just requires a pointer to “character type,” not a specific type such as char or const char or volatile char.

(We might further wonder whether, if we were implementing our own function like snprintf and using <stdarg.h> to do it, whether passing a char * argument and processing it with a va_arg(ap, const char *) macro invocation would work. My initial reading of the va_arg specification in 7.16.1.1 2 says the types must be compatible, but char * and const char * are not compatible, but I have not studied this thoroughly.)

Footnote

1 Technically, a string literal is a thing in the source code or the representation of it during phases of C translation, and it is used to create an array of char. For simplicity, I will refer to array as the string literal.

Community
  • 1
  • 1
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312