6
\$\begingroup\$

Seeking code correctness and best practices.

I would like to re-allocate various amounts of memory (0 or more) such that on systems that may return NULL for an allocation of 0 does not falsely imply a memory allocation error.

Given:

  • void * (with a valid value or NULL already)
  • size_t (new desired size)

Receive:

  • void * (new pointer)
  • int (failure status)

#include <assert.h>
#include <stdlib.h>

int ReallocAndTest(char **Buf, size_t NewSize) {
  assert(Buf);
  char *NewBuf = realloc(*Buf, NewSize);
  if ((NewBuf == NULL) && (NewSize > 0)) {
    return 1;  // return failure
  }
  *Buf = NewBuf;
  return 0;
}

I came across the below references, which imply a realloc() return of NULL is not always a failure. The lone special case occurs with size is 0. The return value may or may not be NULL.

C11dr 7.22.3.5 The realloc function 4 The realloc function returns a pointer to the new object (which may have the same value as a pointer to the old object), or a null pointer if the new object could not be allocated.

C11dr §J.1 Unspecified behavior. — The amount of storage allocated by a successful call to the calloc, malloc, or realloc function when 0 bytes was requested (7.22.3).

C11dr §J.3.12 Implementation-defined behavior. Whether the calloc, malloc, and realloc functions return a null pointer or a pointer to an allocated object when the size requested is zero (7.22.3).


[Answer]

  1. Use void* rather than char *.

  2. With a 0 size, avoid returning the implementation defined
    ..a) NULL or
    ..b) ptr that cannot be referenced.
    and return a pointer to insure only 1 of the 2 is ever returned.

On point #2, I only slightly favor returning NULL.

\$\endgroup\$
2
  • \$\begingroup\$ I already see I should be using void * rather than char *. \$\endgroup\$ Commented Dec 4, 2013 at 21:45
  • \$\begingroup\$ For semantic consistency, you might want to change 0 to FALSE, 1 to TRUE and int to bool(C++) or bool_t/BOOL/BOOLEAN (or whatever standard bool type you're using in your C program). That will make it more clear to callers what they should expect the function to do. \$\endgroup\$
    – Matt
    Commented Dec 8, 2013 at 10:00

2 Answers 2

6
\$\begingroup\$
  1. Standard naming convention in C is usually lower_case_with_underscore.

  2. You should be using void * rather than char *

  3. I would consider making the return value deterministic when the new size is 0 by always returning NULL rather than the implementation defined behaviour (which could technically be a pointer which is not NULL but still not allowed to be dereferenced). free guarantees by spec that free(NULL) is legal.

\$\endgroup\$
5
  • \$\begingroup\$ I am unfamiliar with the standard in 1. Where may I find it? \$\endgroup\$ Commented Dec 5, 2013 at 0:09
  • \$\begingroup\$ @chux: en.wikipedia.org/wiki/… and stackoverflow.com/questions/1722112/… \$\endgroup\$
    – ChrisWue
    Commented Dec 5, 2013 at 6:20
  • \$\begingroup\$ The wiki says "standard library identifiers are mostly lowercase" and this is not a standard library identifier. Sorry if the "wrapper" description misled. The SO post reflect various conventions but not a standard. The C standard does distinguish between upper and lower case letter in all identifiers, so this implies that upper case usage is not verboten. The SO post did stress consistency. I concur with your naming convention of trying to be standard. \$\endgroup\$ Commented Dec 6, 2013 at 18:54
  • 2
    \$\begingroup\$ Yes, naming conventions are always subjective. However PascalCase for local variables and function parameters is rather unusual. \$\endgroup\$
    – ChrisWue
    Commented Dec 7, 2013 at 0:12
  • \$\begingroup\$ This is a good "patch critique" for the given wrapper. The other answer explains a better wrapper. \$\endgroup\$ Commented Feb 3, 2015 at 12:20
5
\$\begingroup\$

With the variety of behaviors exhibited by different implementations, I think the only safe thing is to test for zero size before the realloc call and set a minimum allocation (eg 1 byte).

Note that realloc will probably set errno to ENOMEM when it fails, but I don't think this is guaranteed.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ ENOMEM appears to be in Linux and not the C standard. It is surprising that a memory allocation error does not generate a error code per the C standard. Maybe a history thing. \$\endgroup\$ Commented Dec 5, 2013 at 0:15
  • 3
    \$\begingroup\$ +1 Checking for 0 before calling realloc is indeed the correct approach. Avoid writing code that relies on unspecified or impl.defined behavior. One of the point of writing a wrapper is to get rid of such things. \$\endgroup\$
    – Lundin
    Commented Dec 10, 2013 at 11:53
  • 1
    \$\begingroup\$ I (also) suggest if someone calls your_malloc() or your_realloc() with size 0, you should call malloc() or realloc() with size 1. That way you always have a distinct pointer. Some code may benefit from the assumption that distict allocated objects have distinct addresses; and of course it's not good to be confused between success and failure! I did this in my malloc and realloc wrappers: sam.nipl.net/libb/alloc.b Additionally, this allows your_malloc and your_realloc to be signature compatible with the originals. \$\endgroup\$ Commented Feb 3, 2015 at 12:13

Not the answer you're looking for? Browse other questions tagged or ask your own question.