#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.
Related
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
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) {
}
I like to know the possible bug in the following possible wrapper function of the realloc C function:
void reallocX(void** ptr, size_t size)
{
void *new_ptr = realloc(*ptr, size);
if (new_ptr != NULL)
{
if (new_ptr != *ptr)
{
*ptr = new_ptr;
}
}
else
{
printf( "Help! realloc returned NULL!\n");
exit( EXIT_FAILURE);
}
}
.
I know that it is based in the possible wrapper function of the malloc C function, that is:
void *
mallocX (size_t nbytes)
{
void *ptr;
ptr = malloc (nbytes);
if (ptr == NULL) {
printf( "Help! malloc returned NULL!\n");
exit (EXIT_FAILURE);
}
return ptr;
}
. This malloc C function wrapper function is based on the "mallocc" C function of this webpage.
Thanks for any help.
A null-pointer return value from realloc may indicate either:
That the function did not allocate storage.
That the size parameter was zero and the memory was deallocated.
Your code covers the first possibility, but not the second. A null return value can be valid, indicating that deallocation of a memory block completed succesfully. Signalling an error under these conditions is a bug.
Note this applies for C90 C++98
As noted in the comments, passing a size parameter of 0 is implementation specific in C99/C11 C++11.
There is at least one bug; this line:
if (new_ptr != *ptr)
invokes undefined behavior. Any use of the old pointer, especially comparison with the new one, after realloc succeeds, is forbidden. You need to make the assignment unconditionally.
Further, while this is not an internal bug, the external API is misdesigned and error-prone. Often the type of the caller's pointer will not be void * but some other pointer type like char * or struct foo *, and you'll have users doing:
reallocX((void **)&myptr, size);
This causes your function to invoke undefined behavior via aliasing violations (at the very least; possibly also out-of-bound writes if different pointer types vary in size/representation) because it's accessing a void * object at an address that does not point to a void * object but to some other type of pointer object.
These kinds of wrappers are simply harmful and need to be rejected. There's no way to do them right/safely.
i use simple bool pointer class member. Assigning false resp. true behaves different - see comments in code.
I outcomment one to test the other below.
I use compiler call g++ -o basic basic.cpp
class Test
{
public:
int a;
bool* abool;
};
int main() {
Test t;
//t.abool = false; // WORKS
//t.abool = true; // ERROR: cannot convert 'bool' to 'bool*' in assignment - expected IMO;
// this should work for both values IMO
//*(t.abool) = true; // Segmentation fault
//*(t.abool) = false; // Segmentation fault
cout << t.abool << endl;
return 0;
}
That is because a conversion exists from false to 0. So it becomes t.abool = 0; which is initializing abool with NULL. However, true converts to 1 and since you can't initialize the pointer with an absolute integer, you get a compiler error. In the second case (with comment this should work IMO) you will get a seg fault as you are trying to dereference an unitialized pointer which invokes undefined behavior.
You're bool* hasn't been initialized to point to anything.
You can assign it a null pointer value (which false will convert to), or a valid pointer to a bool object:
bool test;
t.abool = 0; // or NULL
t.abool = &test;
Once it's pointing at an actual bool object you can assign bool value to that object through the pointer:
*t.abool = true;
*t.abool = false;
false works because false is probably seen as 0 by g++
Any literal of numeric type and value zero can be implicitly converted to a null pointer. bool is a numeric type, and false has value zero. Non-zero values (such as true) can't be implicitly converted to a pointer.
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.