Reputation: 222
I came across an example of non-portable C code where a char pointer is an argument to a variadic C function. The example is described in the image below. The part highlighted in blue is not necessarily clear, and appears wrong. In particular, I have two questions:
Assuming that NULL was 32-bit int 0 on a system, wouldn't compiler do an implicit cast of 32-bit - int to 64-bit 0 when it encounters char *string = NULL
. If not, then are we saying that each expression like char *string = NULL
is non-portable and must be always replaced with an explicit cast like char *string = (char *)NULL
for portable C?
If NULL was 32-bit int 0, and char *string was 64-bit then why would printf run out of bits to print like it is suggested in the blue highlight. Shouldn't printf get full 64 bits as it was passed string and not NULL.
Noncompliant Code Example (NULL)
The C Standard allows NULL to be either an integer constant or a pointer constant. While passing NULL as an argument to a function with a fixed number of arguments will cause NULL to be cast to the appropriate pointer type, when it is passed as a variadic argument, this will not happen if
sizeof(NULL) != sizeof(void *)
. This is possible for several reasons:
- Pointers and ints may have different sizes on a platform where NULL is an integer constant
- The platform may have different pointer types with different sizes on a platform. In that case, if NULL is a void pointer, it is the same size as a pointer to char (C11 section 6.2.5, paragraph 28), which might be sized differently than the required pointer type.
On either such platform, the following code will have [undefined behavior][1]:
char* string = NULL; printf("%s %d\n", string, 1);
On a system with 32-bit int and 64-bit pointers,
printf()
may interpret the NULL as high-order bits of the pointer and the third argument 1 as the low-order bits of the pointer. In this case,printf()
will print a pointer with the value0x00000001
and then attempt to read an additional argument for the%d
conversion specifier, which was not provided.Compliant Solution (NULL)
This compliant solution avoids sending
NULL
toprintf()
:char* string = NULL; printf("%s %d\n", (string ? string : "null"), 1);
(Source)
Upvotes: 0
Views: 695
Reputation: 222763
The referenced article is wrong and should be disregarded.
- Assuming that NULL was 32-bit int 0 on a system, wouldn't compiler do an implicit cast of 32-bit - int to 64-bit 0 when it encounters
char *string = NULL
.
An assignment automatically converts the right operand to the type of the left operand. So char *string = NULL
will convert the NULL
value to char *
, not to “64-bit 0”.
If not, then are we saying that each expression like
char *string = NULL
is non-portable and must be always replaced with an explicit cast likechar *string = (char *)NULL
for portable C?
No, char *string = NULL
is portable C code; it is strictly conforming.
- If NULL was 32-bit int 0, and char *string was 64-bit then why would printf run out of bits to print like it is suggested in the blue highlight. Shouldn't printf get full 64 bits as it was passed string and not NULL.
The code referenced, char* string = NULL;
followed by printf("%s %d\n", string, 1);
, does not pass NULL
to printf
. It passes string
to printf
, and the prior assignment converts NULL
to char *
. So printf
is passed a char *
that has the value of a null pointer. This will not cause any problem in interpreting the variable arguments to printf
. (It is, however, improper to pass a null pointer for the %s
conversion.)
If the call were instead printf("%s", NULL);
, then there is a problem. Arguments corresponding to the ...
part of a variable-argument function are not automatically converted to a parameter type. They are processed by the default argument promotions, which largely promote narrow integer types to int
and promote float
to double
, but they will not convert an int
to any type of pointer. Thus, if NULL
is defined as 0
, then printf("%s", NULL);
passes an int
where a char *
is expected, and this may cause various misinterpretations of the arguments.
In consequence, never use the NULL
macro as a direct argument to a function with a variable argument list. Using a pointer variable that has been assigned from NULL
is okay.
Upvotes: 2