2

I've looked all over and it seems that there are a lot of mixed views on assert. For example, if I'm malloc'ing a pointer and want to make sure it's been allocated correctly I'd write:

p = malloc(sizeof(int));
assert(p)

instead of:

p = malloc(sizeof(int));
if (p == NULL)
{
... send error message
}

I know that with assert it will end the program, but for testing purposes — what I want to know is what the absolute safest way of:

  1. testing for things like a malloc being done correctly.
  2. dealing with an error if something isn't malloc'd correctly.
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Finlandia_C
  • 385
  • 6
  • 19
  • Bear in mind `assert` is often a no-op in release mode. – juanchopanza Dec 28 '15 at 00:45
  • definitely option 2, `assert` is for debugging purpose. – Enamul Hassan Dec 28 '15 at 00:47
  • Well, if you want to check errors in release mode, obviously not. – juanchopanza Dec 28 '15 at 00:47
  • @juanchopanza: "Release mode" does not mean the same thing to different people. The important thing here is whether `NDEBUG` is defined. Some people never define `NDEBUG`. – Dietrich Epp Dec 28 '15 at 00:47
  • Assertions can be omitted by adding `-DNDEBUG` to the compiler command line (or any other mechanism to `#define NDEBUG`); they are not safe because a program will crash or burn with a null pointer error (probably — not guaranteed by the standard). The best way to deal with it is to check with an `if` statement. If that's too onerous, write a wrapper function such as `void *xmalloc(size_t nbytes)` which calls `malloc()` and checks the return and prints a message and exits if there's a problem — and then call the wrapper everywhere instead of plain `malloc()`. It's OK to assert before testing. – Jonathan Leffler Dec 28 '15 at 00:48
  • @DietrichEpp I was purposefully vague. Often `NDEBUG` is set as part of some configuration and the user isn't even aware of it. – juanchopanza Dec 28 '15 at 00:49
  • I'm for the second method. The `NULL` pointer must be checked even when the code is supposedly "bug free", and remedial action taken. If there is unexpectedly not enough memory for the operation, you need to handle it gracefully, such as "please close some applications and try again". Or, say, an embedded data logger needs to announce "memory full, please delete some test results" - and so on, rather than just say "oops sorry switching off". You won't get paid for the product! – Weather Vane Dec 28 '15 at 01:39
  • @WeatherVane If, for example, the pointer could not be malloc'd, what would be the appropriate course of action? If there's no space to allocate memory surely an assert result would be similar to a check for NULL? Both indicate a lack of space to allocate? – Finlandia_C Dec 28 '15 at 01:40
  • 1
    My point was that when you remove `assert` for release, you still need to do something, not nothing. You never know why a function may fail unexpectly, that's why error checking is *de rigueur*. – Weather Vane Dec 28 '15 at 01:49
  • So if the code is still being tested - a failure to assert is a problem that cannot really be handled eloquently? Assert fits the bill for testing? – Finlandia_C Dec 28 '15 at 01:50
  • 2
    If you need to explicity test for `NULL` in the final version, you might as well do it from day 1. I've never used `assert`. – Weather Vane Dec 28 '15 at 01:52
  • IMO the major "problem" with asserts is that they are _too_ tempting, as they identify the _exact problem_ (output "text" checked by the compiler!) with virtually no programming effort! If only there were equivalent "bail" functions which cannot be turned off . . . – m4r35n357 Apr 10 '23 at 09:13

2 Answers2

3

Recall assert() is normally only active in debug build of programs and not release versions.

Classify the potential errors into groups:

  1. Run-time errors that must be handled.
    assert() is no good here, instead code must handle the error.

  2. Run-time errors that should be handled yet have no defined remedy.
    assert() is not wise here either. Code should signal (leave error message) and exit.

  3. Run-time errors with no simply re-course to handle them.
    assert() could be used here. Now when the program faults/dies/hangs we are left with nothing. Recommend code should signal like in #2 if at all possible.

  4. Compile time errors.
    assert(sizeof(int)*CHAR_BIT >= 32) is a good example usage. It is reasonable to assume that a build will occur in debug mode. Even this carries a risk with deployed source code that a user in the field may skip the debug build, so suggest only use assert() for internal code. Yet this is only expected to show at time time.
    Even better, use static_assert() to show the error at compile time.


assert() is a tool in the C toolbox. It has its uses - and mis-uses.


With malloc(), I have worked on many projects that barred direct use of C lib malloc() and instead used a project specific code like my_malloc_never_fail() and my_malloc_may_fail() which had error handling and metrics. As @Weather Vane commented, good error handle is challenging.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

  1. TESTING FOR THE FAILURE:
    The malloc function is REQUIRED by the C standard to return NULL if the requested amount of memory cannot be given to the program.

    That means that if the return value of malloc is non-NULL, you can be sure that ALL of the memory was properly allocated.

    Checking for a NULL return value is the ONLY way to determine whether or not malloc was successful. The assert function can be used to stop the program if an assertion fails, but in a production release of the program, there must be other error handling.

  2. HANDLING THE FAILURE:
    If the return value is NULL, use the errno variable to determine WHY the failure occurred. The errno variable is also part of the C standard.

    For LINUX, here is the list of values errno can be set to:

    http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html

  3. IMPORTANT: A malloc failure is a serious error. If this happens in the program execution, don't try to continue to execute additional functionality in the program. Stop (exit from) the program as soon as an error has been logged and reported to the user of the program, as follows:

    You should use the exit function with a non-zero return value to notify the user of the program that the program exited with an error status. The exit function is ALSO part of the C language standard.

    Also, before you exit the program, make sure all other memory that was allocated (prior to the malloc failure) is properly de-allocated.

A B
  • 4,068
  • 1
  • 20
  • 23
  • 1
    Detail: Note that with `n==0`, C allows `malloc(n)` to return `NULL` and it is _not_ an out of memory condition. `errno` is not necessarily changed in that case. Linux and other OS may specify otherwise. – chux - Reinstate Monica Dec 28 '15 at 03:02