5

When I learnt C99 I was told to Always check the return value of malloc to check whether it succeeded or failed, but now I started learning C++ and I was told that there is no need to do this with the keyword new, and you can suppose that it will always work for you.

But why is that?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 1
    With modern you should limit your use of `new` and really concider if you need it. For buffers use `std::vector`; owned object use `std::unique_ptr` etc. – Richard Critten May 17 '20 at 10:26
  • 2
    Because `new` throws an exception instead. – user207421 May 17 '20 at 10:28
  • 1
    Removed the C tag. This is a C++ question fundamentally, even if C is presented for reference. – StoryTeller - Unslander Monica May 17 '20 at 10:29
  • Don't use `malloc()` in C++ `Malloc()` will never `throw` anything, as it is a C function, while `new` does. Exception processing is not available in C, so the only similar thing you can use is the exception handling in C# (which has nothing to do in common with C or C++) – Luis Colorado May 18 '20 at 18:30
  • As a side note, most code I've seen doesn't bother trying to handle `std::bad_alloc` exceptions, and instead just lets the process terminate. There are several reasons for that: (1) On many/most modern OS's, running out of RAM will cause the process to be killed outright anyway, and not during a call to `new`, so catching `bad_alloc` won't help, (2) if you're out of RAM, there's probably not much your process can do to recover anyway, and (3) allowing the process to terminate will free up RAM. Some programs go further and have a parent process around to re-launch the program if it exits. – Jeremy Friesner Sep 15 '21 at 00:41

5 Answers5

4

new can still fail and throw an std::bad_alloc exception, and your program needs to may check whether it did, or simply let the exception propagate up. There is also a flag you can pass to new to make it act like malloc and return NULL on error. Take a look at the documentation.


Edit Here are two examples:

try {
  char* arr = new char[20];
} catch (std::bad_alloc& e) {
  // Handle error
}

Or using the nothrow flag, making new act like malloc:

char* arr = new (std::nothrow) char[20];
if (!arr) {
  // Handle error
}
uvuv
  • 368
  • 1
  • 7
  • I don't know if you are familiar with C# but there using try and catch too often to catch errors make your program slow and affect performance, is that true for C++ true? –  May 17 '20 at 10:56
  • 1
    @dark_smith This is (a) irrelevant if the API or operator is *designed* to throw exceptions, as you don't have any choice but to catch them, and (b) urban myth which was exploded 25 years ago, long before C# was ever thought of. – user207421 May 17 '20 at 11:00
  • 2
    "your program needs to check whether it did. " - it doesn't need to check. The exception can be left to propagate up. This is the whole idea of exceptions. – M.M May 17 '20 at 11:11
  • This answer implies that every explicit use of `new`, and every construct that invoke `new`, should be placed in a try-catch block. Oftentimes there is no recovery from a failed memory allocation. An out of memory condition might well mean that even trying to construct a meaningful error message will fail. The common approach is to forego the try-catch and simply let the exception propagate upward. – David Hammen May 17 '20 at 11:49
  • @M.M well, you can have your process just crash, but normally *somewhere* in your program you'd like to handle the exception, maybe do some cleanup, and exit gracefully. Of course if you're writing a library function you can just define it as throwing the `std::bad_alloc` exception and let whoever uses the function handle that exception. – uvuv May 17 '20 at 11:50
  • @uvuv - How do you exit gracefully from a memory allocation failure? Typically there is no graceful recovery as the program needs the requested memory. In c++, wrapping every dynamic memory allocation, including implicit ones such as `vector foo(1000000000000ull)`, is considered bad form. Instead, simply let the exceptions bubble up. Note that a very common response in C to a call to `malloc` that returns null is to invoke `abort`. Letting `std::bad_alloc` exceptions bubble up, unhandled, is the equivalent, but a lot less verbose. – David Hammen May 17 '20 at 12:10
  • I see your point, but I do think there are cases where you'd like to do some logging or cleanup, and for that you'll need to catch the exception - edited accordingly – uvuv May 17 '20 at 12:23
  • @uvuv - You cannot reliably do logging in the catch block to a `std::bad_alloc` exception. And cleanup should not be needed, assuming one is using RAII. Moreover, that `new` (or `malloc`) appears to have worked does not necessarily mean that it actually did work on a modern computer with virtual memory, where "actually did work" means that every element of the allocated chunk of memory can be written to / read from. It is quite possible for `malloc(large_number)` to return non-null but writing halfway into the returned buffer will fail. – David Hammen May 17 '20 at 13:24
2

Any dynamic allocation can fail.

malloc signals this by returning NULL. If it failed, unless explicitly you check for its return the program will continue, even though malloc failed, most likely resulting in Undefined Behavior when you try to access via the pointer returned by malloc (which is NULL). This is why you should always check for malloc failure.

new on the other hand signals this by throwing an std::bad_alloc exception (default behavior). If you don't catch the the exception it will bubble up to the top and terminate your program. This is desired, so you don't need to do anything.


Also please note that in C++ you should never explicitly call new/delete. Use standard containers like std::vector or smart pointers.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • `new` is still needed with smart pointers. It is `delete` that is the sign of a old-style program rather than `new`. – David Hammen May 17 '20 at 11:08
  • 1
    @DavidHammen Not true, smart pointers can and should be initialized without explicit `new` – M.M May 17 '20 at 11:12
  • @M.M - For shared pointers, yes, that is the recommended practice. But for unique pointers, (1) `std::make_unique` doesn't exist in c++11 (many projects have not migrated beyond c++11), and (2) `std::make_unique`, if available, is but a very thin wrapper around `new` with no advantage other than that it hides the use of `new`. – David Hammen May 17 '20 at 11:29
  • 1
    @DavidHammen https://stackoverflow.com/questions/22571202/differences-between-stdmake-unique-and-stdunique-ptr-with-new – M.M May 17 '20 at 12:46
1

New allocates memory and calls constructor for object initialization: if it fails it throws an exception std::bad_alloc. malloc allocates memory and does not call constructor: if its allocation fails, it return a null pointer, so you have to check what you get from it. However, in c++ you cannot assume that new will always work: you can assume that if it doesn't work, it throws an exception.

Wippo
  • 853
  • 8
  • 22
  • 1
    The `new` operator is also backed by a standard library function. It is *that* library function that throws an exception on allocation failure. – StoryTeller - Unslander Monica May 17 '20 at 10:26
  • @StoryTeller-UnslanderMonica Does the standard say that it's backed by a library function, or is that an implementation detail? The integer division operator, for example, could be backed by a library function in some implementations. – Keith Thompson May 17 '20 at 10:28
  • @KeithThompson - Yes it says that. It also discusses how client code may displace them. – StoryTeller - Unslander Monica May 17 '20 at 10:29
  • The answer has nothing to do with library functions. – user207421 May 17 '20 at 10:31
  • Your edit now says it both throws an exception and returns null if it fails. This problem is caused by overuse of 'it' to refer to multiple subjects without an apparent change of subject. 'Library function' still has nothing to do with it. Libraries can throw exceptions, and there is no such function as `Malloc()`. – user207421 May 17 '20 at 10:54
  • @user207421 I wanted to make it clearer but I ended up complicating it, my bad. – Wippo May 17 '20 at 11:02
  • @StoryTeller-UnslanderMonica you mean the *`new` expression*, not the "`new` operator". The latter *is* a standard library function, which is called by the new expression – M.M May 17 '20 at 11:12
  • @M.M - No, I mean the "new opertor". What you mean is the standard library function named "operator new". – StoryTeller - Unslander Monica May 17 '20 at 11:19
  • @StoryTeller-UnslanderMonica To be painfully precise, the standard (C++17 8.3.4 [expr.new]) says "A **new-expression** may obtain storage for the object by calling an allocation function", and later "An implementation is allowed to omit a call to a replaceable global allocation function. When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression." If I'm reading this correctly, a conforming implementation can implement `new` by magic, without calling any function. – Keith Thompson May 18 '20 at 22:08
  • @Keith - That wording is part of a proposal that had different goals in mind, eliding dynamic allocation whenever possible. Either way, it's still the allocation function that's specified to throw `bad_alloc`, not the new expression. – StoryTeller - Unslander Monica May 18 '20 at 22:17
  • @StoryTeller-UnslanderMonica That wording is copy-and-pasted from the C++17 standard. (I don't have a copy of the official standard. It's from the `c++17` tag in the [git repo](https://github.com/cplusplus/draft). I see the same wording in the n4141 draft, which as I understand it matches the published C++14 standard. C++11 (I do have an official copy of that) has different wording: "A *new-expression* obtains storage for the object by calling an allocation function." – Keith Thompson May 18 '20 at 23:14
  • @Keith - Yes, said proposal was voted into C++14 if memory serves. It was to make standard what Clang did with `__builtin_operator_new`. – StoryTeller - Unslander Monica May 18 '20 at 23:19
0

There are many ways to check the return values,

  • you could check the returned pointer if you use the nothrow version
  • you can use a try block on a much higher level
  • you can use the set_new_handler to check handle it for all new's.

I prefer the 2nd and 3rd.

Surt
  • 15,501
  • 3
  • 23
  • 39
0

In C++, unless you use advanced features, if memory cannot be allocated with the new operator, an exception is thrown, so the there is no need to check if the pointer obtained by new is null or not. Handling the exception is up to the programmer. If you don't, the program will terminate abruptly. It is actually tricky to handle this exception properly and restart the operations without memory or resource leaks, which is why allocation of objects with new and delete is now considered obsolete. Using containers and smart pointers is a better alternative.

Note that you can get the same behavior in C with a wrapper on malloc():

#include <stdio.h>
#include <stdlib.h>

void *xmalloc(size_t size) {
    void *p = malloc(size);
    if (p == NULL) {
        fprintf(stderr, "malloc failed for %zu bytes\n", size);
        exit(1);
    }
    return p;
}

There is no need to check for memory allocation failure by xmalloc() since such a failure causes an abrupt program termination automatically. This approach can be used for command line utilities where failure is not catastrophic and can be handled interactively.

chqrlie
  • 131,814
  • 10
  • 121
  • 189