I know that NULL is always 0, but why does the following code print the message?
#include <iostream>
using namespace std;
int main() {
int* ptr = nullptr;
if (ptr == 0) {
cout << "Does it always work?";
}
return 0;
}
Yes.
A pointer initialised from nullptr is a null pointer.
Comparing a null pointer to the literal 0 (or to a std::nullptr_t, which nullptr is; together these are null pointer constants) yields true, always.
http://eel.is/c++draft/basic.fundamental#14
http://eel.is/c++draft/conv.ptr#1
You can't do this with any old expression, though; even if integer i is 1, i-i is not a valid null pointer constant, despite evaluating to 0 at runtime. Your
program will not compile if you try to do this. Only the literal 0 is a valid null pointer constant that can be compared to pointers.
Also, that does not necessarily mean that it has all bits zero, though! Much like how converting a bool to int gives you zero or one, but the actual underlying bits of the bool can be whatever the implementation likes.
Finally, note that your terminology is slightly off; per [support.types.nullptr/1], nullptr itself has no address that can be taken
Related
#define NULL 0
class Sample{
public:
Sample():m_nNumber(0){}
Sample(int nNumber):m_nNumber(nNumber){}
~Sample(){}
int GetNumber(){return m_nNumber;}
void SetNumber(int nNumber){m_nNumber = nNumber;}
private:
int m_nNumber;
};
Sample* TestSample(Sample* mSample){
int nNumber = mSample->GetNumber();
if ( nNumber%2 == 0) return mSample;
return NULL;
}
void TestNumber ( int nSample )
{
Sample mTest(nSample);
Sample* mTest2 = TestSample(&mTest);
if ( mTest2 ) /* Is this statement valid? or an Undefined Behaviour? */
{
std::cout<<"Sample is Even"<<std::endl;
}
else
{
std::cout<<"Sample is Odd"<<std::endl;
}
}
int main()
{
TestNumber( 10 );
TestNumber( 5 );
}
Given the following snippet I have a few Questions.
Is it an undefined behavior to return a 0 (NULL) from a function that returns a class pointer?
When doing a Boolean if statement on a pointer to a User defined class (if it's even allowed or does not result in an undefined behavior), does it evaluate the address of the pointer, or (EDIT) the value of the pointer?
I am using VS2008 due to the code I'm working on is a Legacy Source and it doesn't have updated third party Libraries.
Both is perfectly valid and though some might argue better ways exist to handle this, still used frequently.
NULL is a valid value for a pointer.
Checking if a pointer is NULL or not is valid (that is what an if statement is doing in C++, checking if the value given is not zero).
What is not "allowed" is dereferencing a NULL pointer. So any usage of it beyond checking whether it is NULL would result in some ugly behaviors.
Is it an undefined behavior to return a 0 (NULL) from a function that returns a class pointer?
It is not undefined behavior. The literal 0 is a "null pointer constant", and it can be used to initialize a value of any pointer type.
When doing a Boolean if statement on a pointer to a User defined class (if it's even allowed or does not result in an undefined behavior), does it evaluate the address of the pointer, or the contents of the address?
if(mTest2) is a valid test. As you're not dereferencing the pionter mTest2, this checks whether the pointer is null or not and not anything about the content.
If on the other hand, you were to dereference a null pointer, then that would lead to undefined behavior.
When you initialize a pointer with nothing(NULL), that element still has a memory address big enough for the initialising type of that pointer(4 bytes for int, 1 for char etc.) but why,since it's tehnically nothing, not even the value zero? I mean, NULL can't be a fixed value like 0 because zero still is considered a value, so it is something more than that?
Example:
#include <iostream>
int *a=NULL;
int main()
{
std::cout <<&a; //it will show the address in hexadecimal system;
return 0;
}
Your program does not answer the question you were asking. This program shows that, yes the pointer has an address, it needs one to store the value (the address it is pointing to). When you print the value you see that it is indeed nullptr (since this is C++ not C).
#include <iostream>
int *a= nullptr;
int main()
{
std::cout << &a << '\n'; // Will show the address OF THE POINTER in hexadecimal system;
std::cout << a << '\n'; // Will show the address at a is pointing to.
return 0;
}
Output:
0x601180
0
I'm pretty sure there's a duplicate, but I don't see any now. You confuse the meaning of operators * and & in different contexts.
Here, &p means "address of p". And what is p? p is a global variable of pointer type. It is perfectly valid to take address of any global variable.
So, to clear things up:
#include <iostream>
int *a=NULL;
int main()
{
std::cout << &a; //perfectly valid, address of p, type int** (pointer-to-pointer-to-int)
std::cout << a; //still valid, it gives address to where p is pointing, i.e. 0 (NULL)
std::cout << *a; //wrong, dereferencing an invalid address, there's no memory allocated
return 0;
}
You also seem to have few misconceptions about pointers:
"that element still has a memory address big enough for the initialising type of that pointer(4 bytes for int, 1 for char etc.)"
Not at all. Pointer is just a pointer. It doesn't care where does it point to. In fact, on lower levels it's just plain int. It can point to an array, to an element, to nothing at all or to some wild place where nothing was ever stored.
"NULL can't be a fixed value like 0 because zero still is considered a value, so it is something more than that?"
Again, pointer is just a pointer. Pointer doesn't know anything at all about value. Value may or may not exist, and the memory where pointer points to may or may not be valid. And in fact, NULL is defined to be exactly 0 (or nullptr in newer standards): https://en.cppreference.com/w/cpp/types/NULL
I'm reading c++ code, where the developer often uses this kind of pattern:
float *_array;
//...
while (expression) {
if (_array) {
// ...
_array += 1;
} else {
// ...
}
}
The outer while loop will terminate independently from where _array points to. My question is about the if (_array) condition and the incrementation within that clause.
I first thought that it should check if the pointer "ran out of" the array, but that does not seem to be case. I tested it with this simple snippet:
float *p = new float[5];
int i = 0;
for (i = 0; i < 10; i++) {
if (p) {
std::cout << "ok\n";
} else {
std::cout << "no\n";
}
p += 1;
}
This will print 10 times "ok". So if (pointer) evaluates to true even if the pointer exceeded the defined array length.
But what else could be the purpose of if (pointer) in that context?
Its purpose is to check whether pointer _array is pointing to NULL or not, i.e. to check if it is a NULL pointer.
new throws std::bad_alloc exception and therefore no need to check NULL. In case of malloc, calloc, realloc or new(std::nothrow), if the allocation fails they return NULL. In such case you need to check for NULL.
In C and C++ a pointer when evaluated in a boolean context returns true if the pointer is not NULL and false if the pointer is NULL.
The form if (ptr) ... is a common shorthand for if (ptr != NULL) ....
This interpretation rule is true even if in the specific platform a NULL pointer is not represented by binary zeros.
The null pointer is implicitly converted into boolean false and the non-null pointers are converted into true.
So you can use either
if(_array != NULL)
or
if(_array)
ยง 4.12 Boolean conversions
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.
Adding to the main answer: your second code causes undefined behaviour.
Once the loop runs 6 times, p moves past the end of the array and causes undefined behaviour. Pointers may only point to an object, or one past the end, or be null pointers.
This shows why "trial and error" is not a good way to learn about C++ because it is hard to tell the difference between defined behaviour, and undefined behaviour that happens to do what you expect this time.
The code is flawed - the pointer is incremented even if it's NULL.
#include <iostream>
using namespace std;
int main()
{
float *ptr = nullptr;
for(int i = 0; i < 5; ++i)
{
cout << (ptr ? "ok" : "no") << endl;
++ptr;
}
cin.get();
}
Output:
no
ok
ok
ok
ok
You'll get access violation if you try to dereference that pointer. Put the pointer incrementation in the if(ptr) clause and it will stay NULL if it was NULL before the loop.
As far as I understand, in C, if condition works on whether the value is zero or non-zero. Since NULL pointer has a value of zero, it fails the if condition! In other words, this method can be interpreted as a verification for NULL pointer
Proof
#include <stdio.h>
int main()
{
int *p = NULL;
int a = 1;
printf("%p %d\n", p, p);
p = &a;
printf("%p %d\n", p, p);
return 0;
}
Now when we compile & run this:
$> gcc null.c
$> ./a.out
(nil) 0
0x7fff0556ff3c 89587516
Notice that in the first printf, with the %d, the value of the pointer p is zero. Hence if you use a pointer directly in a if condition, it will act as a true or false, which can be interpreted as NULL pointer or non-NULL pointer.
Best Practice
Also, I would like to add that the usage is from the standard best practices. This is the preferred method for checking if the pointer is NULL as compared to the potentially dangerous & error-prone:
if (p == NULL) {
}
which can be mis-typed (dangerously) into:
if (p = NULL) {
}
int main()
{
int* nir = new int; // creating dynamic memory
*nir = 7; // assigning value
cout << *nir << endl;
delete nir; // deleting
nir = 0; // **is this line for assigning the address nir=0? 0 is also part of memory right? Why didn't we put NULL?
*nir = 8; // **can i do like this and change the value, so that the output can be 8 now?
cout << *nir << endl;
delete nir;
nir = 0;
return 0;
}
This is the code that I created to understand new. But, even though it was compiled fine by Code::Blocks, during the runtime, it crashes. I have two questions, which I have already mentioned in comment part.
nir = 0;
is this line for assigning the address nir = 0? 0 is also part of memory right? Why didn't we put nir = NULL?
*nir = 8;
can I do like this and change the value, so that the output can be 8 now? After all, I have already deleted the *nir value.
nir=0;
This sets the pointer to NULL. 0 and NULL are the same in this context.
*nir=8
This is wrong as nir in not a valid pointer. It's no suprise that it crashes!
cout<<*nir<<endl;
This is also wrong as nir is invalid pointer. You cannot read or write.
delete nir;
This is harmless, as deleting a NULL pointer is safe (it does nothing).
This code snippet is wrong
nir=0; //**is this line for assigning the address nir=0? 0 is also part of memory right? Why didn't we put NULL?
*nir=8; //**can i do like this and change the value, so that the output can be 8 now?
cout<<*nir<<endl;
delete nir;
nir=0;
You did not allocate memory and are trying to write to address 0.
*nir=8; //**can i do like this and change the value, so that the output can be 8 now?
Usually the program will crash.
As for the line
nir = 0;
then it is equivalent to
nir = NULL;
In C++ NULL usualy defined as 0 or ( long )0 and so on.
According to the C++ Standard
1 A null pointer constant is an integer literal (2.14.2) with value
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.
You tagged c++ so I recommend using nullptr instead of 0/NULL
nir = nullptr;
The problem
The literal 0 (which is essentially of type int) also serves as a null pointer literal in C++. This kludge results in ambiguity and bugs.
Solution
Use the nullptr keyword instead of 0 to indicate a null pointer value
source
A short breakdown of errors you purposely committed:
int main()
{
int* nir = new int; // allocating dynamic memory
*nir = 7; // assigning value
cout << *nir << endl;
delete nir; // deleting
nir = 0; // **is this line for assigning the address nir=0?
// 0 is also part of memory right? Why didn't we put NULL?
The previous comment is wrong. For historical reasons, assigning a literal 0 to a pointer variable means setting it to a null pointer constant. This is not guaranteed to be 0 [!!!]. NULL and nullptr_t are more modern...
*nir = 8; // **can i do like this and change the value,
// so that the output can be 8 now?
cout << *nir << endl;
On some systems you can do that. But your computing platform is now irretrievably corrupted. Modern systems catch the culprit and raise General Protection Fault, which only kills your program.
delete nir;
Because programmers are keen to avoid useless work, the above (delete NULL) is defined as a no-op
nir = 0;
return 0;
The previous two lines are useless, as nir is never used again and main per standard returns 0 unless it explicitly doesn't, quite in contrast to any other function.
}
Similar to this question:
XCode 6.3 Warning : Comparison of address of 'myObject' not equal to null pointer is always true
with C++, I found that previously working code for evaluating null pointers stopped working:
struct AStruct
{
int x, y;
char *name;
};
AStruct& GetStruct()
{
return *(AStruct*)0;
}
int main(int argc, const char * argv[]) {
AStruct& mys = GetStruct();
if ( ! &mys) {
printf("null pointer \n");
}
else
{
printf("NOT a null pointer\n");
}
return 0
}
This always prints out
NOT a null pointer
I've tried other ways of pointer-to-reference checking:
if ( &mys == NULL)
if ( &mys == nullptr )
None of these worked.
Then I noticed the warning:
Reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false
But there are no suggested fixes.
What is the canonical way of checking null pointers these days?
You are not checking against a pointer, you are checking against a reference.
References are not supposed to be nullptr since they must refer to an existing value. Indeed what you are doing *(AStruct*)0 is not well defined since a reference shouldn't be able to generate undefined behaviour through "dereferencing it" but in this way you could trigger the problem.
If client code has a AStruct& then it can be sure that the reference points to something, you are breaking this condition.
If you need to work with references that can be null use a pointer, eg
AStruct* GetStruct()
{
return nullptr;
}
if (!GetStruct())
{
...
The fact that the code worked in a previous version of Xcode is a symptom of the fact that this is not well-defined code.
You don't have a pointer there; mys is a reference. Then when you check the value of &mys, you are checking the address of the reference.