0

Update: I have been digging in various ways to come to a better understanding of the picture I have demonstrated below(a deliberately buggy program!). I am still learning but hate to move forward with a misunderstanding in my brain. I have found the following link and it seems to lead in a promising direction:

conversion from nullptr_t to bool: valid or not?


I tried testing memory allocated with new and ran into unexpected behavior. I have created code to demonstrate:

#include <iostream>
#include <new>

using namespace std;

int main()
{
    // This section is checking the supposed nullptr assigned when memory allocation fails.
    int i = 2000000000; // Within range of long values.
    int * p2 = new (nothrow) int[i];

    cout << "Allocation innappropriate amount of memory and getting nullptr?" << endl << endl;
    cout << "Pointer address: " << p2 << " --- Value at pointer address: " << *p2 << endl << endl;

    if(p2)
    {
        cout << "True - Pointer address: " << p2 << " --- Value at pointer address: " << *p2 << endl << endl << endl;
    }
    else
    {
        cout << "False -Pointer address: " << p2 << " --- Value at pointer address: " << *p2 << endl << endl << endl;
    }

    // This section is checking how nullptr looks in gcc.
    int * p1 = nullptr;
    cout << "Assigning nullptr to pointer varialble." << endl << endl;

    if(p1)
    {
        cout << "True - Pointer address: " << p1 << " --- Value at pointer address: " << *p1 << endl << endl << endl;

    }
    else
    {
        cout << "False -Pointer address: " << p1 << " --- Accessing value would cause error!" << endl << endl << endl;
    }

}

The output is:

Allocation innappropriate amount of memory and getting nullptr?

Pointer address: 0x1f70783e040 --- Value at pointer address: 0

True - Pointer address: 0x1f70783e040 --- Value at pointer address: 0


Assigning nullptr to pointer varialble.

False -Pointer address: 0 --- Accessing value would cause error!



Process returned 0 (0x0)   execution time : 0.096 s
Press any key to continue.

I can't boolean test the pointer in the second part without dereferencing the pointer which I understand isn't supposed to be necessary. Is this just different interpretation of the standard or is this a bug?

  • 2000000000 ints (8 GB) isn't an "inappropriate" amount of memory on modern computers. The allocation succeeds, and the values in the newly allocated memory are indeterminate - it's just by "luck" that it happens to be zero. – Nate Eldredge Apr 02 '23 at 23:38
  • The behavior of your program looks perfectly normal to me, and I don't understand what your concern is. Your `if (p1)` test doesn't dereference the pointer, and will be true iff `p1` is not a null pointer. – Nate Eldredge Apr 02 '23 at 23:41
  • The problem is p1 will always be true and you might cause undesired results when you access memory that does not exist. – WackEyedWanderer Apr 02 '23 at 23:45
  • You mean `p2`? No, it will be null (and your test will take the false branch) if the allocation actually fails for lack of memory. You may have to try it with a much larger size though (use `size_t` instead of `int` for the size). It's also possible that your OS has [overcommit](https://unix.stackexchange.com/questions/441364/what-is-the-purpose-of-memory-overcommitment-on-linux) enabled, in which case you do have the issue you mentioned - there are pros and cons, but if you don't like it, turn it off. That's nothing to do with gcc, anyway. – Nate Eldredge Apr 02 '23 at 23:48
  • An address is never 0. – WackEyedWanderer Apr 02 '23 at 23:49
  • 1
    I agree that an address is never 0 (or more precisely, that the null pointer is never the address of any object). I don't see how that's relevant, though. Could you explain more carefully what behavior you expected from the program, and why? – Nate Eldredge Apr 02 '23 at 23:51
  • I don't want to access memory that has not been allocated. I am given to understand that there is a difference between NULL and nullptr and gcc seems to have two different versions of nullptr. – WackEyedWanderer Apr 02 '23 at 23:54
  • Your program as it stands is accessing memory that **has** been allocated. The allocation **succeeded** and `p2` is **not** a null pointer of any kind. Now if it did fail, then you would take the `false` branch, and the `*p2` in that branch would crash the program. But that's your bug - don't dereference a pointer that you know to be null. – Nate Eldredge Apr 02 '23 at 23:55
  • The difference between `NULL` and `nullptr` is irrelevant to this program, but they are both going to give you a null pointer, for which `if (p)` will be false. Always. 100% guaranteed. Moreover, on any reasonable system that gcc supports, outputting a null pointer is always going to show the value `0`. Your `0x1f70783e040` isn't some weird kind of null pointer; it's a **non-null, valid** pointer. – Nate Eldredge Apr 03 '23 at 00:00
  • Oh, I missed that your second `cout` actually has a `*p2` in it. Obviously you should test whether the pointer is null *before* doing that. Is your concern that the compiler didn't stop you from writing it? C++ compilers are generally perfectly happy to let you shoot yourself in the foot, that's not a bug. – Nate Eldredge Apr 03 '23 at 00:03
  • See this test: https://godbolt.org/z/nrP69daPE. If the number assigned to `i` is reasonably small, the allocation succeeds, we take the `True` branch, and the memory is accessible. If you assign a huge number to `i`, like 2000000000000, then a null pointer is returned and we properly take the `False` branch. In that case, it is up to us to know not to try to dereference `p2`. – Nate Eldredge Apr 03 '23 at 00:09
  • I have to or it will always be determined to be true. I have already found out that other compilers don't do this and this is why I am asking if it is a difference of interpretation of the standard for nullptr. – WackEyedWanderer Apr 03 '23 at 01:40
  • 1
    I do not understand - where is the unexpected behavior part? What is unexpected? `The problem is p1 will always be true` That is not true, `p1` will be false when the allocation failed. `I am given to understand that there is a difference between NULL and nullptr` No. `if it is a difference of interpretation of the standard for nullptr` _Why_ is this relevant? Are you asking XY questions? – KamilCuk Apr 03 '23 at 06:39

2 Answers2

0

Is this a bug?

There are several. Not in GCC though, but in your program.

cout << "Allocation innappropriate amount of memory and getting nullptr?" << endl << endl;

The answer to this question depends on what kind of computer you are running this program on and in what exact environment. For a regular unaltered PC, the answer is probably "no".

cout << "Pointer address: " << p2 << " --- Value at pointer address: " << *p2 << endl << endl;

You are dereferencing p2 without checking that it is not nullptr. If it happens to be a nullptr (unlikely on your PC, but likely in other environments) this dereference is undefined behaviour. Moreover, dereferencing a pointer that does not point at an initialised object and reading its value is undefined behaviour.1

cout << "True - Pointer address: " << p2 << " --- Value at pointer address: " << *p2 << endl << endl << endl;
...
cout << "False -Pointer address: " << p2 << " --- Value at pointer address: " << *p2 << endl << endl << endl;

These two statements suffer from the same problem. If p2 is nullptr, then dereferencing it is undefined behaviour because dereferencing a nullptr is undefined behaviour. If it is not, then dereferencing it and reading the value reads an int that has not been initialised, which is undefined behaviour.1

Here is how you might test allocation limits on your computer (live demo):

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <new>

int main()
{    
    for (std::size_t i = 1;  ; i <<= 1)
    {
        std::cout << "Trying to allocate 0x" 
           << std::hex << std::setw(16) << 
           << std::setfill('0') << i << " ints... ";
        auto * p2 = new (std::nothrow) int[i];

        if(p2)
        {
            std::cout << "Success:  p2 is " << p2 << "\n";
        }
        else
        {
            std::cout << "Failure:  p2 is " << p2 << "\n";
            break;
        }
    }
}

Note, not dereferencing the pointer, there isn't anything usable hiding inside.

--

1Or it was in one of the earlier versions of C++, I cannot remember. It is definitely useless though. Don't do that.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
0

Finally figured out a couple of things that were causing me confusion.

  1. When you input a number that is too large for the variable(in this case an int) then the maximum value of int seems to be stored in the variable. This allows the program to continue running using this variable.

  2. This value is still messes up std::cin and it goes into an infinite loop. Apparently a fail flag gets set and it can be reset with cin.clear().

I thought this was a problem with pointers. It is instead a case of how c++ will let you be very stupid.