I know the ternary operator has some surprising restrictions, but I was a bit baffled that this fails to compile for me:
void foo(bool b)
{
int* ptr = ((b) ? NULL : NULL);
}
Obviously that's the minimum needed to show the problem. The error is:
[BCC32 Error] Unit11.cpp(20): E2034 Cannot convert 'int' to 'int *'
Compiler is the less-than-100%-conforming Embarcadero C++Builder 2010, so a compiler bug is far from impossible...
NOTE: Parens modified to avoid confusion about my intent.
NOTE2: I'd got myself a little confused about how I'd arrived at this construct in the first place, so here's my excuse: I was getting some compilation errors on a line like a = b? c : d, where b, c and d were all complex expressions. To narrow it down, I replaced c and d with NULLs in order to check if b was the culprit. At this point, everything went to hell in a handcart.
NULL is a macro that expands to 0 (or some integral constant expression with a value of 0, for example, (1 - 1)). It's not otherwise "special."
Any integral constant expression with a value of zero is usable as a null pointer constant, which is the reason that int* ptr = 0; is permitted. However, here, the expression is b ? 0 : 0; this is not an integral constant expression (b is not constant); its type is int, which is not implicitly convertible to int*
The workaround would be to explicitly specify that you want a pointer type:
int* const null_int_ptr = 0;
int* ptr = b ? null_int_ptr : null_int_ptr;
The example is a bit contrived, though: usually when one uses the conditional operator, at least one of the arguments is actually a pointer type (e.g. b ? ptr : 0); when one of the operands is a pointer type, the 0 is implicitly converted to that same pointer type and thus the type of the entire conditional expression is the pointer type, not int.
The only case where you can have this "problem" is where a null pointer constant is used as both the second and third operands of the conditional operator, which is rather odd.
Your problem is that on your system NULL is defined to be 0 which is assumed to be an int in the context of the ternary operator. If you static_cast one of the operands to int* it should auto-promote the other one.
But why are using such a construct in the first place?
NULL could be defined as having type int or even long, so the ternary operator has the same type. There's no implicit conversion to the pointer type, so compiler generates an error.
The gotcha here is that there is an implicit conversion from constant integer expression evaluating to zero (the infamous null pointer constant).
Possible solution here is an explicit cast:
int* ptr = b ? (int*) NULL : NULL;
Related
Am I allowed to use the NULL pointer as replacement for the value of 0?
Or is there anything wrong about that doing?
Like, for example:
int i = NULL;
as replacement for:
int i = 0;
As experiment I compiled the following code:
#include <stdio.h>
int main(void)
{
int i = NULL;
printf("%d",i);
return 0;
}
Output:
0
Indeed it gives me this warning, which is completely correct on its own:
warning: initialization makes integer from pointer without a cast [-Wint-conversion]
but the result is still equivalent.
Am I crossing into "Undefined Behavior" with this?
Is it permissible to utilize NULL in this way?
Is there anything wrong with using NULL as a numerical value in arithmetical expressions?
And what is the result and behavior in C++ for this case?
I have read the answers of What is the difference between NULL, '\0' and 0 about what the difference between NULL, \0 and 0 is, but I did not get the concise information from there, if it is quite permissible and also right to use NULL as value to operate with in assignments and other arithmetical operations.
Am I allowed to use the NULL pointer as replacement for the value of 0?
No, it is not safe to do so. NULL is a null-pointer constant, which could have type int, but which more typically has type void * (in C), or otherwise is not directly assignable to an int (in C++ >= 11). Both languages allow pointers to be converted to integers, but they do not provide for such conversions to be performed implicitly (though some compilers provide that as an extension). Moreover, although it is common for converting a null pointer to an integer to yield the value 0, the standard does not guarantee that. If you want a constant with type int and value 0 then spell it 0.
Am I might crossing into Undefined Behavior with this?
Yes, on any implementation where NULL expands to a value with type void * or any other not directly assignable to int. The standard does not define the behavior of your assignment on such an implementation, ergo its behavior is undefined.
is it permissible to operate with the NULL in that way?
It is poor style, and it will break on some systems and under some circumstances. Inasmuch as you appear to be using GCC, it would break in your own example if you compiled with the -Werror option.
Is there anything wrong about to use NULL as numerical value in arithmetical expressions?
Yes. It is not guaranteed to have a numerical value at all. If you mean 0 then write 0, which is not only well defined, but shorter and clearer.
And how is the result in C++ to that case?
The C++ language is stricter about conversions than is C and has different rules for NULL, but there, too, implementations may provide extensions. Again, if you mean 0 then that's what you should write.
NULL is some null pointer constant. In C it could be an integer constant expression with value 0 or such an expression cast to void*, with the latter more likely. Which means you can't assume to use NULL interchangeably with zero. For instance, in this code sample
char const* foo = "bar";
foo + 0;
Replacing 0 with NULL is not guaranteed to be a valid C program, because addition between two pointers (let alone of different pointer types) is not defined. It will cause a diagnostic to be issued due to a constraint violation. The operands for addition will not be valid.
As for C++, things are somewhat different. Lack of an implicit conversion from void* to other object types meant that NULL was historically defined as 0 in C++ code. In C++03, you could probably get away with it. But since C++11 it can legally be defined as the nullptr keyword. Now again producing an error, since std::nullptr_t may not be added to pointer types.
If NULL is defined as nullptr then even your experiment becomes invalid. There is no conversion from std::nullptr_t to an integer. That is why it is considered a safer null pointer constant.
Am I allowed to use the NULL pointer as a replacement for the value of 0?
int i = NULL;
The rules vary between languages and their versions. In some cases you can and in others, you can't. Regardless, you shouldn't. If you're lucky, your compiler will warn when you attempt it or even better, fail to compile.
In C++, prior to C++11 (quote from C++03):
[lib.support.types]
NULL is an implementation-defined C++ null pointer constant in this International Standard.
It makes little sense to use a null pointer constant as an integer. However...
[conv.ptr]
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.
So, it would technically work even if it's nonsensical. Due to this technicality, you may encounter poorly written programs that abuse NULL.
Since C++11 (quote from latest draft):
[conv.ptr]
A null pointer constant is an integer literal ([lex.icon]) with value zero or a prvalue of type std::nullptr_t.
A std::nullptr_t is not convertible to an integer, so using NULL as integer would work only conditionally, depending on choices made by the language implementation.
P.S. nullptr is a prvalue of type std::nullptr_t. Unless you need your program to compile in pre-C++11, you should always use nullptr instead of NULL.
C is a bit different (quotes from C11 draft N1548):
6.3.2.3 Language / Conversions / Other operands / Pointers
3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. ...
So, the case is similar to post C++11 i.e. the abuse of NULL works conditionally depending on choices made by the language implementation.
Yes, though depending on the implementation you may need a cast. But yes, it is 100% legitimate, otherwise.
Although it is really, really, really bad style (needless to say?).
NULL is, or was, actually not C++, it is C. The standard does however, like for many C legacies, have two clauses ([diff.null] and [support.types.nullptr]) which technically make NULL C++. It is an implementation-defined null-pointer constant. Therefore, even if it's bad style, it's technically as C++ as it can be.
As pointed out in the footnote, possible implementations could be 0 or 0L, but not (void*)0.
NULL could, of course (the standard doesn't explicitly say so, but it's pretty much the only choice remaining after 0 or 0L) be nullptr. That's almost never the case, but it is a legal possibility.
The warning that the compiler showed to you demonstrates that the compiler is in fact not compliant (unless you compiled in C mode). Because, well, according to the warning, it did convert a null pointer (not nullptr which would be of nullptr_t, which would be distinct), so apparently the definition of NULL is indeed (void*)0, which it may not be.
Either way, you have two possible legitimate (i.e. compiler not broken) cases. Either (the realistic case), NULL is something like 0 or 0L, then you have "zero or one" conversions to integer, and you are good to go.
Or NULL is indeed nullptr. In that case you have a distinct value that has guarantees about comparison as well as clearly-defined conversions from integers, but unluckily not to integers. It does, however, have a clearly-defined conversion to bool (resulting in false), and bool has a clearly-defined conversion to integer (resulting in 0).
Unluckily, that's two conversions, so it's not within "zero or one" as pointed out in [conv]. Thus, if your implementation defines NULL as nullptr, then you will have to add an explicit cast for your code to be correct.
From the C faq:
Q: If NULL and 0 are equivalent as null pointer constants, which should I use?
A: It is only in pointer contexts that NULL and 0 are equivalent. NULL should not be used when another kind of 0 is required, even though it might work, because doing so sends the wrong stylistic message. (Furthermore, ANSI allows the definition of NULL to be ((void *)0), which will not work at all in non-pointer contexts.) In particular, do not use NULL when the ASCII null character (NUL) is desired. Provide your own definition
http://c-faq.com/null/nullor0.html
Disclaimer: I don't know C++. My answer is not meant to be applied in the context of C++
'\0' is an int with value zero, just 100% exactly like 0.
for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;
In the context of pointers, 0 and NULL are 100% equivalent:
if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;
are all 100% equivalent.
Note about ptr + NULL
The context of ptr + NULL is not that of pointers. There is no definition for the addition of pointers in the C language; pointers and integers can be added (or subtracted). In ptr + NULL if either ptr or NULL is a pointer, the other must be an integer, so ptr + NULL is effectively (int)ptr + NULL or ptr + (int)NULL and depending on the definitions of ptr and NULL several behaviours can be expected: it all working, warning for conversion between pointer and integer, failure to compile, ...
No, not anymore preferred to use NULL(old way of pointer initilization).
Since C++11:
The keyword nullptr denotes the pointer literal. It is a prvalue of type std::nullptr_t. There exist implicit conversions from nullptr to null pointer value of any pointer type and any pointer to member type. Similar conversions exist for any null pointer constant, which includes values of type std::nullptr_t as well as the macro NULL.
https://en.cppreference.com/w/cpp/language/nullptr
Actually, std::nullptr_t is the type of the null pointer literal, nullptr. It is a distinct type that is not itself a pointer type or a pointer to member type.
#include <cstddef>
#include <iostream>
void f(int* pi)
{
std::cout << "Pointer to integer overload\n";
}
void f(double* pd)
{
std::cout << "Pointer to double overload\n";
}
void f(std::nullptr_t nullp)
{
std::cout << "null pointer overload\n";
}
int main()
{
int* pi; double* pd;
f(pi);
f(pd);
f(nullptr); // would be ambiguous without void f(nullptr_t)
// f(0); // ambiguous call: all three functions are candidates
// f(NULL); // ambiguous if NULL is an integral null pointer constant
// (as is the case in most implementations)
}
Output:
Pointer to integer overload
Pointer to double overload
null pointer overload
int func(int n)
{return n;}
int main()
{ cout << func[4] ;
cout << func[4,3,5] ;}
what do these actually mean? I guess it is about accessing func+4 and func is allocated space on calling func[4].
But, func[4,3,5] is just absurd.
The reason this code compiles and func[4] is not a syntax error is:
1.Function types can implicitly convert to pointers of the same type.
So, if we have code like this:
int f(int);
using func_t = int(*)(int);
void g(func_t);
we can write
g(f)
and aren't forced to write g(&f). The &, taking us from type int(int) to int(*)(int) happens implicitly.
2.In C (and necessarily in C++ for compatibility) pointers are connected to arrays, and when p is a pointer p[x] is the same as *(p + x). So func[4] is the same as *(func + 4).
3.*(p+x) has the type of a function int(int), but again can implicitly decay to a pointer type whenever necessary. So *(func + 4) can implicitly just be (func + 4).
4.Pointers of any type are streamable to std::cout.
Note, that just because it isn't a syntax error doesn't mean it is valid. Of course it is undefined behavior, and as the compiler warning emitted by gcc and clang indicates, pointer arithmetic with a function pointer is generally wrong, because you cannot make an array of functions. The implementation places functions however it likes. (You can make an array of function pointers but that is something else entirely.)
Edit: I should correct myself -- this answer is not entirely correct. func[4] is not valid, because the pointer is not a pointer to an object type. #holyblackcat answer is correct, see his answer for reference in the standard.
This code should be ill-formed, and gcc only compiles it without an error because they are using a nonstandard extension by default. Clang and msvc correctly reject this code.
I'm surprised no answer mentions it, but:
The code in the question is simply not valid C++.
It's rejected by Clang and MSVC with no flags. GCC rejects it with -pedantic-errors.
a[b] (in absence of operator overloading) is defined as *(a + b), and the builtin operator + requires the pointer operand to be a pointer to an object type (which functions pointers are not).
[expr.add]/1
...either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral or unscoped enumeration type.
(Emphasis mine.)
GCC compiles the code because an extension allowing arithmetic on function pointer is enabled by default.
Due to function-to-pointer decay, func[4] is treated as &(&func)[4], which effectively means &func + 4, which (as the link explains) simply adds 4 to the numerical value of the pointer. Calling resulting pointer will most likely cause a crash or unpredicatble results.
std::cout doesn't have an overload of << suitable for printing function pointers, and the best suitable overload the compiler is able to find is the one for printing bools. The pointer gets converted to bool, and since it's non-null, it becomes true, which is then printed as 1.
Lastly, func[4,3,5] has the same effect as func[5], since in this context , is treated as an operator, and x , y is equal to y.
Since it has not been mentioned yet: func[3, 4, 5] is identical to func[5] - the commas in there are the builtin comma operator which evaluates the left hand side expression, discards it and then evaluates the right hand side expression. There is no function call happening here and the commas in the code are not delimiting function parameters.
Yes,It is about accessing the func+4 which is not already defined leading to a garbage value.So the compiler will indicate you with the following warning message.
hereProgram: In function 'int main()':
Program:7: warning: pointer to a function used in arithmetic
It is said in the C standard that
Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type “array of type” is converted to an expression
with type “pointer to type” that pointer to the initial element of the
array object and is not an lvalue.
So why does the array type expression not decay into a pointer when used as the left operand of an assignment operator, like:
int arrayoften[] = { 10, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
arrayoften = 300; //not valid
I mean, the expression arrayoften is not one of the three exceptions, so shouldn't it theoretically be converted to a pointer?
One way of answering this (as #Rakete1111 noted in a comment) is to say that that an array on the lhs does decay to a pointer. But it's a pointer value (an rvalue), not an lvalue, so you can't assign to it.
What's the difference between an rvalue and an lvalue? An rvalue is anything you can compute, that has a value. An lvalue is a value that additionally has a location (that the compiler knows), such that it can be stored into as the left-hand side of an assignment operator. (The term "rvalue" is therefore a backformation; it's something that can appear on the right-hand side.)
We can understand this better with an example: Suppose I have
int a[10], b[10];
int *p1 = a, *p2 = b;
p1 = p2 + 1;
Now, the type p1 is "pointer to int", and the type of the expression p2 + 1 is "pointer to int", so I can make that assignment, no problem.
But what if I said
p1 + 1 = p2; /* WRONG */
The types on the left and right-hand sides are still both "pointer to int", but now the lhs is p1 + 1, which is an rvalue, but not an lvalue. It has no location, it's just a computed value, I can't assign to it.
So, going back to your example, saying
a = p1;
is sort of the same thing. By the language in the C standard you quoted, the expression is converted to
&a[0] = p1;
Again, the types match, but &a1[0] is not an lvalue, so you can't assign to it.
You can tell this is what's going on (or what might be going on) by looking at the compiler error messages: for one compiler I tried, the error was "illegal lhs of assignment operator", which I believe indicates that everything was fine until the compiler discovered that the lhs was not an lvalue. Indeed, I believe that the error message from Ritchie's original C compiler was simply "not an lvalue". (I should boot up my '11 and see.)
Now, with all of this said, it's only one interpretation. That there are others is suggested by the error messages of other compilers:
"incompatible types in assignment"
"incompatible types when assigning to type ‘int[10]’ from type ‘int’"
"array type 'int [10]' is not assignable"
Compiler 1 is an older version of gcc, compiler 2 is a newer version of gcc, and compiler 3 is clang. It looks like these compilers are (for whatever reason) not blindly converting the array on the lhs to a pointer. Perhaps this is so that they can give a better error message, perhaps it's for some other reason, although it does seem to suggest that these compilers are adding another exception -- not explicitly listed in the Standard -- to the list of cases where arrays are not converted to pointers.
[As an aside, though, I'm not sure what rule these newer compilers are using to cause the lhs to remain an array. It can't be, "don't convert arrays to pointers on the left-hand side of an assignment operator", because there are plenty of valid expressions -- such as *(a + 1) = 10 -- where that conversion is fine.]
One more point this whole discussion brings up is that the deep intertwinedness of the arrays-decay-to-pointers rule is that it makes it very difficult to contemplate a future in which there's an extension to C that adds proper array assignment. Any compiler that wants to make
int a[10], b[10];
a = b;
work, first has to figure out how not to convert it into
a = &b[0];
or
&a[0] = &b[0];
I was reading code from the cpp-btree library of google (https://code.google.com/p/cpp-btree/) and I came accross that compile-time assert mechanism.
// A compile-time assertion.
template <bool>
struct CompileAssert {
};
#define COMPILE_ASSERT(expr, msg) \
typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
So I understand more or less what it does, if expr is evaluated to false by the compiler it will declare a new type msg that will be a CompileAssert < false > array of size -1 which will trigger a compilation error.
What I don't get is the bool(expr) part, what is this exactly? Some kind of call to the copy constructor of the class bool? (but it's a builtin type so I'm confused)
I though this would be a mechanism to raise a compilation error when expr is not a boolean but actually I managed to compile a short program whit that line
COMPILE_ASSERT("trash",error_compilation_assert);
It compiles just fine with gcc 3.4
So can anyone explain the bool(expr) part of the mechanism?
It's a type conversion. There are 3 main types of type conversions in C++:
Cast notation (C-style cast): (bool) expr
Functional notation (constructor-style cast): bool(expr)
Cast operators (C++-style cast); static_cast<bool>(expr)
Cast notation and functional notation are semantically equivalent (i.e. they both perform the strongest possible conversion, the C-cast), but the scope & precedence of the functional notation is clearer.
It is generally advised not to use them in C++ code and use the specific cast operators (const_cast, static_cast etc.) instead.
So in your code, it's just a way of forcing the value to type bool and enclosing it in parentheses at the same time, so that no operator priority issues arise.
bool(expr) casts expr into a bool.
The first parameter should be some kind of expression, such as a == b. Using a string literal here is useless.
bool(expr) is a function-style cast which converts the expression to a bool. Lots of things convert implicitly to bool, but I guess they wanted an explicit cast to make sure the result is a bool.
If you convert a pointer to a bool, it evaluates to false if it is a NULL pointer, or true otherwise. Your string literal "Trash" decays into a const char * to the first character. As this is not a null pointer, the expression evaluates to true.
bool(expr) tries to convert expr to bool either implicitly or using any user defined conversion operator.
In C++, you can instantiate built-in types with ctor syntax:
bool b1 = bool(true);
bool b2 = bool(b1);
The difference to:
bool b2 = b1;
is that the latter does an implicit conversion to bool. When such an implicit conversion isn't allowed (as in the template typedef), then bool(b1) makes it explicit by creating a temporary bool from b1 and the temporary doesn't have to be converted anymore; it's an actual bool type.
I found a bug in my code where I compared the pointer with '\0'.
Wondering why the compiler didn't warn me about this bug I tried the following.
#include <cassert>
struct Foo
{
char bar[5];
};
int main()
{
Foo f;
Foo* p = &f;
p->bar[0] = '\0';
assert(p->bar == '\0'); // #1. I forgot [] Now, comparing pointer with NULL and fails.
assert(p->bar == 'A'); // #2. error: ISO C++ forbids comparison between pointer and integer
assert(p->bar[0] == '\0'); // #3. What I intended, PASSES
return 0;
}
What is special about '\0' which makes #1 legal and #2 illegal?
Please add a reference or quotation to your answer.
What makes it legal and well defined is the fact that '\0' is a null pointer constant so it can be converted to any pointer type to make a null pointer value.
ISO/IEC 14882:2011 4.10 [conv.ptr] / 1:
A null pointer constant is an integral constant expression prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion.
'\0' meets the requirements of "integral constant expression prvalue of integer type that evaluates to zero" because char is an integer type and \0 has the value zero.
Other integers can only be explicitly converted to a pointer type via a reinterpret_cast and the result is only meaningful if the integer was the result of converting a valid pointer to an integer type of sufficient size.
'\0' is simply a different way of writing 0. I would guess that this is legal comparing pointers to 0 makes sense, no matter how you wrote the 0, while there is almost never any valid meaning to comparing a pointer to any other non-pointer type.
This is a design error of C++. The rule says that any integer constant expression with value zero can be considered as the null pointer constant.
This idiotic highly questionable decision allows to use as null pointer '\0' (as you found) but also things like (1==2) or even !!!!!!!!!!!1 (an example similar to one that is present on "The C++ programming language", no idea if Stroustrup thinks this is indeed a "cool" feature).
This ambiguity IMO even creates a loophole in the syntax definition when mixed with ternary operator semantic and implicit conversions rules: I remember finding a case in which out of three compilers one was not compiling and the other two were compiling with different semantic ... and after wasting a day on reading the standard and asking experts on c.c.l.c++.m I was not able to decide which of the three compilers was right.