Let's consider the next code:
#include <stdlib.h>
int some_api_function_with_multiple_results( int** OUT_result ) {
*OUT_result = malloc( 123 * sizeof(**OUT_result) );
return 42;
}
int main(void) {
const int* numbers; /* don't ask me why it's 'const' please, I'm just curious! */
int some_result = some_api_function_with_multiple_results( (int**)&numbers );
free( (void*)numbers );
return EXIT_SUCCESS;
}
Notice the const int*
pointer (NOT int* const
!!) that is passed by reference to the function expecting a pointer to int*
(that is, int**
) as a variable for an additional return value.
This compiles fine and without any warnings with GCC 9.4.0 in C90 mode (-std=c90
). And the C90 standard (ansi-iso-9899-1990-1.pdf
) states the following:
6.1.2.5 Types
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements.16 Pointers to other types need not have the same representation or alignment requirements.
16 The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.
It's worth noting that C89 draft (http://port70.net/~nsz/c/c89/c89-draft.html#3.1.2.5) imposes a much more restrictive constraint. I wonder if this is the difference between the draft standard and its final version, or is it between C89 and C90? (update: I found a scanned copy of C89 called fipspub160.pdf
and it has the same wording as in C90, so it appears to be a draft inaccuracy)
3.1.2.5 Types
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Other pointer types need not have the same representation or alignment requirements.
It's also well-known that a value of type T*
can be assigned to a variable (or passed as a function parameter) of type const T*
just fine and without any explicit coercion such as typecasting:
3.3.16.1 Simple assignment
One of the following shall hold:42
- 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;
42 The asymmetric appearance of these constraints with respect to type qualifiers is due to the conversion (specified in 3.2.2.1) that changes lvalues to "the value of the expression" which removes any type qualifiers from the top type of the expression.
3.3.2.2 Function calls
If the expression that denotes the called function has a type that includes a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters.
The actual thing that is bothering me is that const int*
and int*
types are not compatible according to both C89 and C90 standards (and C99 as well, so it wasn't an errata that could be fixed in both intermediate Technical Corrigenda or numerous Defect Reports of the standardization committee). So I'm not sure if the following statements make my code having undefined behaviour:
3.5.4.1 Pointer declarators
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
3.5.3 Type qualifiers
For two qualified types to be compatible, both shall have the identically qualified version of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers does not affect the specified type.
3.1.2.6 Compatible type and composite type
All declarations that refer to the same object or function shall have compatible type; otherwise the behaviour is undefined.
The question is, does it actually take place? Or is it still valid code?