Will a void* always have the same representation as a char* ?
Details:
I want to work with a variadic function that takes char*'s terminated by a (char*)0 like so:
int variadic(char*, ...); //<-prototype
variadic("foo", "bar", (char*)0); //<- usage
I wanted to replace (char*)0 with NULL, but judging from
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf's:
66) The macro NULL is defined in (and other headers) as a
null pointer constant; see 7.19.
3 An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant. 66)
If a null pointer constant is converted to a pointer type, the
resulting pointer, called a null pointer, is guaranteed to compare
unequal to a pointer to any object or function.
I can't, because in the variadic context, I absolutely need a char* and a plain 0 is unacceptable.
If I defined:
#define NIL (void*)0 /*<= never a plain 0*/
would it be legal for me to use it to terminate my variadic(char*,...)?
C11, §6.2.5, ¶28 (draft N1570) says:
A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type. 48) Similarly, pointers
to qualified or unqualified versions of compatible types shall have the
same representation and alignment requirements.
(emphasis mine).
It's specifically allowed to access a void* argument using va_arg(args, char*) and vice versa, not just for the null pointer.
See also http://en.cppreference.com/w/cpp/utility/variadic/va_arg
Related
I included a check for nullptr in a line of C code. The compiler (gcc) complained when using -std=c17 as well as -std=gnu17.
Is there such a thing as nullptr (or equivalent) in modern C standards? (C11, C17)
If not, then why?
No, C still uses NULL for a null pointer.
C++ needs a dedicated null pointer literal because it has overloading and template type deduction. These features get confused by NULL, which expands to 0 (or something like that) in C++. However, the risk of such confusion in C is small (maybe _Generic can get confused by this), and in addition, C can use (void*)0 for a null pointer, which mitigates this risk even more.
The closest thing to C++'s nullptr is C's NULL. Which may be
an integer constant expression with the value 0,
an integer constant expression with the value 0 cast to the type void*.
A null pointer constant may be converted to any pointer type; such conversion results in the null pointer value of that type.
The formal C17 specifications state that the stddef.h header defines NULL "which expands to an implementation-defined null pointer constant." (7.19)
A null pointer constant is defined as follows (6.3.2.3)
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null
pointers shall compare equal.
Note that this makes the following program ambiguous, as NULL could be an integer constant expression (accepted by the function) or of the type void* (not accepted by the function).
#include <stdio.h>
void printInt(int n)
{
printf("%d\n", n);
}
int main(void)
{
printInt(NULL);
}
Which is why nullptr was introduced in C++11. For C, having no function overloading or type deduction, this is less of an issue.
A null pointer in C is a pointer object pointing at "null". You can turn a pointer into a null pointer by assigning it to a null pointer constant. Valid null pointer constants are 0 and (void*)0. The macro NULL is guaranteed to be a null pointer constant.
The internal representation of the pointer then becomes a "null pointer", which could in theory point at an address different from zero on some exotic system. Similarly, NULL could in theory expand to something different from zero in old, pre-standard C.
When creating C++, Bjarne Stroustrup found all of this to be needlessly complex and decided that "NULL is 0" (source: https://www.stroustrup.com/bs_faq2.html#null). Notably C++ was created long before the first standardization of C, so his arguments are less relevant to standard C than they were to pre-standard C.
For more info about null pointers vs NULL in C, see What's the difference between null pointers and NULL?
ISO C 23 now has nullptr, as well as the nullptr_t type. The proposal that introduced it has some rationale.
Will a void* always have the same representation as a char* ?
Details:
I want to work with a variadic function that takes char*'s terminated by a (char*)0 like so:
int variadic(char*, ...); //<-prototype
variadic("foo", "bar", (char*)0); //<- usage
I wanted to replace (char*)0 with NULL, but judging from
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf's:
66) The macro NULL is defined in (and other headers) as a
null pointer constant; see 7.19.
3 An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant. 66)
If a null pointer constant is converted to a pointer type, the
resulting pointer, called a null pointer, is guaranteed to compare
unequal to a pointer to any object or function.
I can't, because in the variadic context, I absolutely need a char* and a plain 0 is unacceptable.
If I defined:
#define NIL (void*)0 /*<= never a plain 0*/
would it be legal for me to use it to terminate my variadic(char*,...)?
C11, §6.2.5, ¶28 (draft N1570) says:
A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type. 48) Similarly, pointers
to qualified or unqualified versions of compatible types shall have the
same representation and alignment requirements.
(emphasis mine).
It's specifically allowed to access a void* argument using va_arg(args, char*) and vice versa, not just for the null pointer.
See also http://en.cppreference.com/w/cpp/utility/variadic/va_arg
I want initialize a function pointer so that it is null. Which of these two ways is preferred?
void (*Pointer)(void) = NULL;
Or
void (*Pointer)(void) = (void (*)(void))0;
0 is implicit convertible to any pointer type. Though how your compiler implements NULL depends.
In your code you can simply write
void (*Pointer)(void) = 0; but it won't be portable , so write void (*Pointer)(void) = NULL;
The standard (ISO/IEC 9899:2011) says:
§6.3.2.3 Pointers
¶1 A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
¶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.
¶3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.66) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
¶4 Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.
…
¶8 A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.
66) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.
§7.19 Common definitions <stddef.h>
¶3 The macros are
NULL
which expands to an implementation-defined null pointer constant; …
All of which adds up to say that either of the notations used in the question is valid and they (and other similar expressions) are equivalent and there will be no problem.
void (*Pointer)(void) = NULL;
void (*Pointer)(void) = (void (*)(void))0;
void (*Pointer)(void) = 0;
Do note that the standard doesn't explicitly say you can convert a pointer to an object type into a pointer to a function type, or vice versa, or that round-tripping is guaranteed to work. Remember, there were once memory models for early Intel machines where data (object) pointers were a different size from function pointers — and that can still happen on other machines (IBM AS/400 and its heirs and successors is another example).
You could also study the questions and answers on the null pointer from http://c-faq.com/.
NULL is defined to be a null pointer constant. A null pointer constant can be used to initialise any pointer (data pointer or function pointer) to a null pointer of the correct type, so assigning NULL is just fine.
0 will also work, because 0 is also a null pointer constant. So will 0L, (13*12-156) and many other ways. NULL is preferred because it shows your intent to assign a null pointer.
The line below that is just unnecessary work.
In the context of another question there was some discussion on whether it was allowed (i.e. would or would not introduce implementation defined or undefined behavior) to cast int** to void** and subsequently assign a value to the dereferenced void*. This brings me to my question on the interpretation of the C11 standard
6.2.5 (28) A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. ...
6.3.2.3 (1) A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
6.3.2.3 (7) ... When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. ...
My question is whether this
int* intptr = NULL;
void* dvoidptr = &intptr; /* 6.3.2.3 (1) */
*(void**)dvoidptr = malloc(sizeof *intptr); /* using 6.3.2.3 (1) */
conforms with the standard or not? It seems strange to me, but I cannot find a conclusive line of argument why not. void* to void** is guaranteed by 6.3.2.3 and 6.2.5 together with 6.3.2.3 help with the alignment.
Code is not valid.
See the emphasis:
6.3.2.3 (1) A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
You are converting int** to void* (fine), and then to different type void** (not ok). C gives no quarantee that you can safely convert void* to anything other than the original type (aside from converting to char *)
In addition, *(void**)dvoidptr results in void* when in reality there is int*. What if, for example, sizeof(void*) == 2 and sizeof(int*) == 1? You can convert void* pointer to other type, but you cannot reinterpret it directly as other type.
This could be thought of as an extension to this question (I'm interested in C only, but adding C++ to complete the extension)
The C11 standard at 6.3.2.3.3 says:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
What my take on this personally is that 0 and (void *)0 represent the null pointer, whose integer value may not actually be 0, but that doesn't cover 0 cast to any other type.
But, the standard then continues:
If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, ...
which covers (int *)0 as null pointer since cast is an explicit conversion (C11, 6.3) which is listed under conversion methods.
However, what still makes me wonder is the following phrase
... or such an expression cast to type void * ...
With the above semantics, this phrase seems completely useless. The question is, is this phrase completely useless? If not, what implications does it have? Consequently, is (int *)0 the null pointer or not?
Another question that can help the discussion is the following. Is (long long)123 considered "123 converted to long long", or "123 with type long long". In other words, is there any conversion in (long long)123? If there is none, then the second quote above doesn't cover (int *)0 as a null pointer.
Short answer:
In both C and C++, (int *)0 is a constant expression whose value is a null pointer. It is not, however, a null pointer constant. The only observable difference between a constant-expression-whose-value-is-a-null-pointer and a null-pointer-constant, that I know of, is that a null-pointer-constant can be assigned to an lvalue of any pointer type, but a constant-expression-whose-value-is-a-null-pointer has a specific pointer type and can only be assigned to an lvalue with a compatible type. In C, but not C++, (void *)0 is also a null pointer constant; this is a special case for void * consistent with the general C-but-not-C++ rule that void * is assignment compatible with any other pointer-to-object type.
For example:
long *a = 0; // ok, 0 is a null pointer constant
long *b = (long *)0; // ok, (long *)0 is a null pointer with appropriate type
long *c = (void *)0; // ok in C, invalid conversion in C++
long *d = (int *)0; // invalid conversion in both C and C++
And here's a case where the difference between the null pointer constant (void *)0 and a constant-expression-whose-value-is-a-null-pointer with type void * is visible, even in C:
typedef void (*fp)(void); // any pointer-to-function type will show this effect
fp a = 0; // ok, null pointer constant
fp b = (void *)0; // ok in C, invalid conversion in C++
fp c = (void *)(void *)0; // invalid conversion in both C and C++
Also, it's moot nowadays, but since you brought it up: No matter what the bit representation of long *'s null pointer is, all of these assertions behave as indicated by the comments:
// 'x' is initialized to a null pointer
long *x = 0;
// 'y' is initialized to all-bits-zero, which may or may not be the
// representation of a null pointer; moreover, it might be a "trap
// representation", UB even to access
long *y;
memset(&y, 0, sizeof y);
assert (x == 0); // must succeed
assert (x == (long *)0); // must succeed
assert (x == (void *)0); // must succeed in C, unspecified behavior in C++
assert (x == (int *)0); // invalid comparison in both C and C++
assert (memcmp(&x, &y, sizeof y) == 0); // unspecified
assert (y == 0); // UNDEFINED BEHAVIOR: y may be a trap representation
assert (y == x); // UNDEFINED BEHAVIOR: y may be a trap representation
"Unspecified" comparisons do not provoke undefined behavior, but the standard doesn't say whether they evaluate true or false, and the implementation is not required to document which of the two it is, or even to pick one and stick to it. It would be perfectly valid for the above memcmp to alternate between returning 0 and 1 if you called it many times.
Long answer with standard quotes:
To understand what a null pointer constant is, you first have to understand what an integer constant expression is, and that's pretty hairy -- a complete understanding requires you to read sections 6.5 and 6.6 of C99 in detail. This is my summary:
A constant expression is any C expression which the compiler can evaluate to a constant without knowing the value of any object (const or otherwise; however, enum values are fair game), and which has no side effects. (This is a drastic simplification of roughly 25 pages of standardese and may not be exact.)
Integer constant expressions are a restricted subset of constant expressions, conveniently defined in a single paragraph, C99 6.6p6 and its footnote:
An integer constant expression96 shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof
operator.
96 An integer constant expression is used to specify the size of a bit-field member of a structure, the value of an enumeration constant, the size of an array, or the value of a case constant. Further constraints that apply to the integer constant expressions used in [#if] are discussed in 6.10.1.
For purpose of this discussion, the important bit is
Cast operators ... shall only convert arithmetic types to integer types
which means that (int *)0 is not an integer constant expression, although it is a constant expression.
The C++98 definition appears to be more or less equivalent, modulo C++ features and deviations from C. For instance, the stronger separation of character and boolean types from integer types in C++ means that the C++ standard speaks of "integral constant expressions" rather than "integer constant expressions", and then sometimes requires not just an integral constant expression, but an integral constant expression of integer type, excluding char, wchar_t, and bool (and maybe also signed char and unsigned char? it's not clear to me from the text).
Now, the C99 definition of null pointer constant is what this question is all about, so I'll repeat it: 6.3.2.3p3 says
An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
Standardese is very, very literal. Those two sentences mean exactly the same thing as:
An integer constant expression with the value 0 is called a null pointer constant.
An integer constant expression with the value 0, cast to type void *, is also a null pointer constant.
When any null pointer constant is converted to a pointer type, the resulting pointer is called a null pointer and is guaranteed to compare unequal ...
(Italics - definition of term. Boldface - my emphasis.) So what that means is, in C, (long *)0 and (long *)(void *)0 are two ways of writing exactly the same thing, namely the null pointer with type long *.
C++ is different. The equivalent text is C++98 4.10 [conv.ptr]:
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.
That's all. "Integral constant expression rvalue of integer type" is very nearly the same thing as C99's "integer constant expression", but there are a few things that qualify in C but not C++: for instance, in C the character literal '\x00' is an integer constant expression, and therefore a null pointer constant, but in C++ it is not an integral constant expression of integer type, so it is not a null pointer constant either.
More to the point, though, C++ doesn't have the "or such an expression cast to void *" clause. That means that ((void *)0) is not a null pointer constant in C++. It is still a null pointer, but it is not assignment compatible with any other pointer type. This is consistent with C++'s generally pickier type system.
C++11 (but not, AFAIK, C11) revised the concept of "null pointer", adding a special type for them (nullptr_t) and a new keyword which evaluates to a null pointer constant (nullptr). I do not fully understand the changes and am not going to try to explain them, but I am pretty sure that a bare 0 is still a valid null pointer constant in C++11.
Evaluating the expression (int*)0 yields a null pointer of type int*.
(int*)0 is not a null pointer constant.
A null pointer constant is a particular kind of expression that may appear in C source code. A null pointer is a value that may occur in a running program.
C and C++ (being two distinct languages) have slightly different rules in this area. C++ doesn't have the "or such an expression cast to type void*" wording. But I don't think that affects the answer to your question.
As for your question about (long long)123, I'm not sure how it's related, but the expression 123 is of type int, and the cast specifies a conversion from int to long long.
I think the core confusion is an assumption that the cast in (int*)0 does not specify a conversion, since 0 is already a null pointer constant. But a null pointer constant is not necessarily an expression of pointer type. In particular, the expression 0 is both a null pointer constant and an expression of type int; it is not of any pointer type. The term null pointer constant needs to be thought of as a single concept, not a phrase whose meaning depends on the individual words that make it up.