compiler allows 'char * k= false'? Why? - c++

I found strange from my point of view compiler behavior, It allows assign Boolean value to * char.
char * k= false;
Why? But after assignment * char is still not initialized. Why compilers doesn't allows assign int value?

It will be implicitly converting the boolean value false to an integer with value zero and as such declaring a NULL pointer. It is effectively no different from
char* k = 0;
which is valid syntax

C++03 Standard, #4.10:
A null pointer constant is an integral constant expression (5.19)
rvalue of integer type that evaluates to zero.
5.19:
An integral constant-expression can involve only literals (2.13),
enumerators, const variables or static data members of integral or
enumeration types initialized with constant expressions (8.5),
non-type tem- plate parameters of integral or enumeration types, and
sizeof expressions.
false is a boolean literal, therefore it falls into the category of a constant expression, so it can qualify as a null pointer constant.

false and true are shortcuts for 0 and 1. For pointer you use NULL which define NULL 0. So it correct syntax.

The compiler allows it because in C++ false is the same as 0 and NULL.
Personally, at least for assignments, I find it easier to understand and more correct to use NULL to indicate a null pointer.
Btw, before C++, on some systems NULL was actually a macro defined as (void*)0xffff; some background on that can be found in this answer.

Related

Why can pointers be NULL

In C or C++, isn't NULL just the constant integer 0? Why are we allowed to assign some pointer to the value NULL if it a constant integer? Wouldn't a compiler say that the two types don't match?
The interpretation of the constant 0 depends on the context. If it's being used in a context where a number is required, it's the number zero. If it's being used in a context where a pointer is required, it's treated as a null pointer. So if you have the code:
int n = 0; // assigns zero
int *p = 0; // assigns null pointer
somefunc((char *)0); // passes null pointer to the function
In C, the macro NULL is permitted to expand into either an integer constant 0 or such a constant cast to void *. In C++ pre-11, it can expand into either an integer constant that evaluates to 0. In C++-11 it can expand into an integer literal with value 0 or a prvalue of type std::nullptr_t (e.g. nullptr).
See http://en.cppreference.com/w/cpp/types/NULL for the C++ description, http://en.cppreference.com/w/c/types/NULL for the C description.
There are two issues:
A type can have a "special", out-of-band, exceptional value. For example, floating-point numbers have the value NaN. And pointers have a special, not-a-valid-pointer value, the null pointer.
What's the name of the null pointer? In C, for better or worse, its name is 0. (It would avoid all kinds of confusion if its name were a separate keyword like null or nil, but that's not the way the language definition came out.)
But yes, you're right, the ability to assign the constant 0 to a pointer variable is a special case, enshrined in the language definition.
6.3.2.3 Pointers
...
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.
66) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.
C 2011 online draft
A literal 0 in a pointer context is treated specially by the compiler, and is understood to be the null pointer constant. In the context of your source code, it behaves like any other zero-valued expression.
However, once your code is translated to machine code, all occurrences of the null pointer constant will be replaced with whatever value the underlying platform uses to represent a well-defined invalid pointer value, whether that’s 0 or 0xFFFFFFFF or 0xDEADBEEF.

Does the 'if' statement in C/C++ cast the operand to an integer?

When you use the if statement in C/C++, or any other logical operator, is the operand you pass to the statement cast to an integer for evaluation?
My current understanding was that the operand passed to the statement is cast to an integer to test whether or not it is non-zero (true), and that if you pass a pointer, this can be cast to an integer to evaluate with a 0/null value being defined as false.
I was under the impression that C++'s standard Bool values were simply typedef of an unsigned char with a value of 0 and 1.
Can anyone explain what's actually happening behind the scenes with this behavior?
In C++ bool is a standalone type that has nothing to do with unsigned char. And in C++ language the expression under if is indeed implicitly cast to bool type. Note: to bool specifically, not to int. For scalar types other than bool, the implicit conversion to bool is essentially defined through non-equality comparison to literal 0. I.e. values that compare non-equal are converted to true, while values that compare equal - to false.
In other words, in C++ statement
if (a)
is interpreted as
bool tmp = (bool) a;
// `tmp` is either `true` or `false`
if (tmp)
which for scalar types is interpreted as
bool tmp = a != 0;
if (tmp)
Note that this works "as expected" for pointer types as well. But instead of converting the pointer to int type, it actually works the other way around: it converts literal 0 to the proper pointer type.
For other types it could be something different, like a call to a user-defined conversion operator operator bool().
In C language the expression under if has to have scalar type. And it is implicitly compared to constant 0. Note that this does not involve converting the controlling expression it to int. Comparison to constant 0 is defined separately for all scalar types. E.g. for int it has its natural meaning, while for pointer types it is interpreted as comparison to null pointer. Now, the result of such comparison in C has type int and evaluates to either 0 or 1. That 0 or 1 is what controls what if branch is taken.
In other words, in C statement
if (a)
is immediately interpreted as
int tmp = a != 0;
// `tmp` is either `0` or `1`
if (tmp)
Additionally, your assumption that null pointer produces a zero int value when converted to int type is incorrect. Neither language makes such guarantee. Null pointer is not guaranteed to be represented by zero address value and is not guaranteed to produce zero value when converted to int.
In C++, the condition in an if statement is converted to a bool.
From the C++11 Standard:
6.4 Selection statements
4 The value of a condition that is an initialized declaration in a statement other than a switch statement is the value of the declared variable contextually converted to bool (Clause 4). If that conversion is ill-formed, the program is ill-formed.
A different section, 4.12 Boolean conversion, of the standard talks about conversions to bool.
4.12/1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false;
any other value is converted to true. A prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.

Is it possible to have a pointer literal?

In C one can have string literals in the form of
char *string = "string here";
integer literals:
uint8_t num = 5;
long literals:
long long bigNum = 90322L;
floating point literals:
float decimal = 6.3f;
Is the a way to have a pointer literal? That is a literal address to a memory space. I am doing some work on an embedded project and need to hard code a value for a DMA access. I am doing something similar to the following:
uint32_t *source = 0x08000000;
While this compiles and works correctly I get the following compiler error (I'm using a variant of GCC):
cc0144: {D} warning: a value of type "int" cannot be used to initialize an entity of type "uint32_t *"
cc0152: {D} warning: conversion of nonzero integer to pointer
Is there a correct way to do this or do I just need to accept this as a fact of C? I know I can do:
uint32_t *source = (uint32_t *)0x08000000;
But that just seems very unnecessary. What is the industry way of doing this? I am also wondering if this feature exists in C++.
In both C and C++ the only pointer literal or constant is zero. We can go to the draft C99 standard section 6.3.2.3 Pointers:
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant.55)
and:
An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be
correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation.56)
the correct way to deal with non-zero integer constant is to use a cast.
The equivalent section from the draft C++ standard would probably be section 5.2.10 Reinterpret cast which says:
A value of integral type or enumeration type can be explicitly
converted to a pointer. A pointer converted to an integer of
sufficient size (if any such exists on the implementation) and back to
the same pointer type will have its original value; mappings between
pointers and integers are otherwise implementation-defined. [ Note:
Except as described in 3.7.4.3, the result of such a conversion will
not be a safely-derived pointer value. —end note ]
You need to see section 3.7.4.3 for all the details.
For the pointer literal reference you need section 2.14.7 Pointer literals which says:
The pointer literal is the keyword nullptr. It is a prvalue of type
std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is
neither a pointer type nor a pointer to member type; rather, a prvalue
of this type is a null pointer constant and can be converted to a null
pointer value or null member pointer value. See 4.10 and 4.11. —end
note ]
No, it's not. That is because literals are valid values, and the only valid pointers are addresses of objects, i.e. the result of address-of operations or of pointer arithmetic on valid pointers.
You could argue that the nullptr keyword furnishes a kind of "pointer literal"; the C++ standard calls it that. It is however the only pointer literal, and ironically it is not of pointer type.
In C++ could you do something like the following?
uint32_t * source = reinterpret_cast<uint32_t*>(0x08000000);

Magic of casting value to bool

Today I realized that casting a value to a bool is a kind of magic:
int value = 0x100;
unsigned char uc = static_cast<unsigned char>(value);
bool b = static_cast<bool>(value);
Both sizeof(uc) and sizeof(b) return 1. I know that uc will contain 0x00, because only the LSB is copied. But b will be true, so my assumption is that, when casting to bool, the value gets evaluated instead of copied.
Is this assumption correct? And is this a standard C++ behavior?
There's nothing magical about it. The conversion from int to unsigned char is defined as value % 256 (for 8-bit chars), so that is what you get. It can be implemented as copying the LSB, but you should still think about it in the semantic sense, not the implementation.
On the same vein, conversion of int to bool is defined as value != 0, so again, that is what you get.
Integral (and boolean) conversions are covered by the C++11 standard in [conv.integral] and [conv.bool]. For the C-style cast, see [expr.cast] and [expr.static.cast].
It is a part of the standard:
4.12 Boolean conversions [conv.bool]
1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer
to member type can be converted to a prvalue of type bool. A zero
value, null pointer value, or null member pointer value is converted
to false; any other value is converted to true. A prvalue of type
std::nullptr_t can be converted to a prvalue of type bool; the
resulting value is false.
Yes, when casting to bool, the value gets evaluated, not copying.
In fact, in your example, as long as value is not 0, b will be true.
Update: Quoting from C++ Primer 5th Edition Chapter 2.1.2:
When we assign one of the nonbool arithmetic types to a bool object, the
result is false if the value is 0 and true otherwise.
According to the rules for C-style casts, (bool)value is effectively a static_cast. The first rule for static_cast then kicks in, which computes the value of a temporary "declared and initialized ... as by new_type Temp(expression);", i.e. bool Temp(value);. That's well-defined: Temp is true iff value != 0. So yes, value is "evaluated" in a sense.
Casting to bool is a feature inherited from plain old C. Originally, C didn't have a bool type, and it was useful to use other types in if statements, like so:
int myBool = 1;
if(myBool) {
// C didn't have bools, so we had to use ints!
}
void* p = malloc(sizeof(int));
if(!p) {
// malloc failed to allocate memory!
}
When you're converting to bool, it acts just like you're putting the statement in an if.
Of course, C++ is backwards compatible with C, so it adopted the feature. C++ also added the ability to overload the conversion to bool on your classes. iostream does this to indicate when the stream is in an invalid state:
if(!cout) {
// Something went horribly wrong with the standard output stream!
}
ISO/IEC C++ Standard:
4.12 Boolean conversions [conv.bool] 1 A prvalue of arithmetic... type can be converted to a prvalue of type bool. A zero value... is converted to false; any other value is converted to true.
So, since a prvalue is a value, you might say that the value gets evaluated, albeit that's kind of pleonastic.

Why is it okay to compare a pointer with '\0'? (but not 'A')

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.