2

In a C book I found in an example for implementing a dynamically resizing array this code (simplified):

void *contents = realloc(array->contents, array->max * sizeof(void *));
array->contents = contents;

memset(array->contents + old_max, 0, array->expand_rate + 1);

Source: Learn C The Hard Way – Chapter 34

I was a bit surprised what memset is supposed to achieve here, but then I understood it's used in order to "zero out" the reallocated memory.

I googled in order to find out, if this is what I'm supposed to do after a realloc and found a stackoverflow answer regarding this:

There is probably no need to do the memset […]

But, even if you wanted to "zero it out so everything is nice", or really need the new pointers to be NULL: the C standard doesn't guarantee that all-bits-zero is the null pointer constant (i.e., NULL), so memset() isn't the right solution anyway.

Source: How to zero out new memory after realloc

The suggested solution instead of memset is then to use a for loop in order to set the memory to NULL.

So my question is, as memset does not necessarily mean setting values to NULL and the for loop solution seems a bit tedious – is it really needed to set the newly allocated memory?

Community
  • 1
  • 1
Max
  • 15,693
  • 14
  • 81
  • 131
  • 2
    IMO this is equivalent to asking about `calloc` vs. `malloc`. If you use `calloc` to allocate and zero memory, then it's consistent to also use `memset` if you need to grow the buffer with `realloc`. – jamesdlin Sep 23 '15 at 06:36
  • I find it strange that you've read that you need to use a for loop, to set every value to NULL instead of just zeroing the memory. I think technically NULL does not have to be equal to 0x0. However, in practice you will be hard pressed to find a compiler/system that doesn't do this. (Can someone back this up?) – Roy T. Sep 23 '15 at 07:02
  • Learn C the Hard Way is [not a great resource](http://hentenaar.com/dont-learn-c-the-wrong-way) according to some, and I am inclined to agree. Consider at least reading some other resources as well, e.g. from [this answer](http://stackoverflow.com/a/562377/696485) the the SO C book list. Note that if you ever decide to read K&R, read with it in one hand and the errata sheets in the other. – Iskar Jarak Sep 23 '15 at 07:21
  • 1
    @RoyT. [NULL has to compare equal to `0` but does not have to be all zero bits](http://stackoverflow.com/a/9894047/696485), although in practice it usually is. – Iskar Jarak Sep 23 '15 at 07:24
  • you should also check return value of realloc, sometimes it fails. – AndersK Sep 23 '15 at 07:29
  • 1
    @RoyT. - It happened on some old systems without memory protection. Physical address 0 could contain operating system info, like interrupt handlers. Having a write to a null pointer overwrite this was a BAD(tm) idea. In that case it might be better to let NULL point to ROM rather than RAM. – Bo Persson Sep 23 '15 at 07:48
  • The title question is "is it common practice?" and a final question is "is it really needed?". The accepted answer answers the 2nd question, but not the title. – chux - Reinstate Monica Sep 23 '15 at 15:14

3 Answers3

4

So my question is, as memset does not necessarily mean setting values to NULL and the for loop solution seems a bit tedious – is it really needed to set the newly allocated memory?

realloc doesn't initialize values of the newly allocated memory segment.

So it is needed to initialize the memory if you are planning to read values of that (uninitialized) memory. Because reading values from that uninitialized memory will trigger undefined behaviour.

By the way, safe way to use realloc (since it can fail) is:

  // Since using realloc with size of 0 is tricky and useless probably
  // we use below check (from discussion with @chux)
  if (new_size == 0) 
    dosmth();
  else 
  {
    new_p = realloc(p, new_size);
    if (new_p == NULL)
    {
      // ...handle error
    }else
    {
      p = new_p;
    }
  }
Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
  • I think it makes actualy sense to initialize the memory, because the initial array was allocated with `calloc`. – Max Sep 23 '15 at 08:53
  • @Max: well, as noted the "new part" of the allocated memory will not be initialized, and if you try to read values from that memory you will trigger UB – Giorgi Moniava Sep 23 '15 at 08:55
0

Setting a void * to 0 is a value that will compare equal to NULL. Likely memset(ptr, 0, size) is OK - but need to see more code for certainty.

OTOH: is the code correct? Maybe should be

// memset(array->contents + old_max, 0, array->expand_rate + 1);
memset(array->contents + old_max, 0, sizeof(void *) * (array->expand_rate + 1) );
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

This is wrong:

But, even if you wanted to "zero it out so everything is nice", or really need the new pointers to be NULL: the C standard doesn't guarantee that all-bits-zero is the null pointer constant (i.e., NULL), so memset() isn't the right solution anyway.

The C standard does in fact guarantee exactly that.

Per section 6.3.2.3 of the C standard:

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

Note that a pointer with a value of zero is a null pointer. That doesn't mean NULL itself has to be zero. But also note that "any two null pointers shall compare equal". So any null pointer is equal to the NULL value.

Since memset() takes as its second argument an integer value, passing a zero integer value to memset() will produce null pointers. Because the value passed to memset() is "[a]n integer constant expression with the value 0", per 6.3.2.3.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • This answer is backwards. The macro `NULL` is guaranteed to be 0 or `(void*) 0`: the null pointer constant. Its *representation* (which is what the compiler translates `0` into in pointer contexts) is not necessarily all-bits-zero, so `memset` does not guarantee producing null pointers. When you give `0` as an argument to `memset`, it is *not* in a pointer context, and it thus will not be converted to a null pointer. Also see http://www.c-faq.com/null/index.html – jamesdlin Sep 24 '15 at 17:57
  • "The macro NULL is guaranteed to be 0 or (void*) 0" **Authoritative** cite? – Andrew Henle Sep 24 '15 at 18:25
  • From the ISO C99 spec, section 6.3.2.3 (which you cited): "An integer constant expression with the value 0, or such an expression cast to type `void *`, is called a *null pointer constant* [55]." combined with footnote 55: "The macro `NULL` is defined ... as a null pointer constant...." – jamesdlin Sep 24 '15 at 22:38
  • Furthermore, the section you cited states "If a null pointer **constant** is **converted** to a pointer type, the resulting pointer, called a **null pointer**..." (emphasis mine). Your answer mixes up the null pointer *constant* (aka `NULL`, aka 0) with a null pointer (not necessarily all-bits-zero). – jamesdlin Sep 24 '15 at 22:46
  • You simply do not understand the standard. **A** *null pointer constant* is not **THE** *null pointer constant*. Given that the standard states that **any two null pointers shall compare equal**, there obviously can be more than one *null pointer constant*. Nothing in the standard requires that any given *null pointer constant* shall be "[a]n integer constant expression with the value 0". All it does is require that an integer value of 0 be treated **as** a *null pointer constant*. There's no way to read any of that as "NULL must be zero". – Andrew Henle Sep 25 '15 at 09:43
  • From the c-faq website you linked to. http://www.c-faq.com/null/confusion4.html "Some implementations naturally represent null pointers by special, nonzero bit patterns, particularly when it can be arranged that inadvertently using those values triggers automatic hardware traps. Requiring null pointers to be represented internally as 0, and therefore disallowing use of the special, nonzero values, would be an unfortunate step backwards..." – Andrew Henle Sep 25 '15 at 09:50
  • Additionally http://www.c-faq.com/null/machexamp.html: "The Prime 50 series used segment 07777, offset 0 for the null pointer, at least for PL/I. Later models used segment 0, offset 0 for null pointers in C, necessitating new instructions such as TCNP (Test C Null Pointer), evidently as a sop to all the extant **poorly-written C code** which **made incorrect assumptions**." – Andrew Henle Sep 25 '15 at 09:59
  • But, since the standard does now require that a constant zero value be treated as a null pointer, we *de facto* have reached a situation where zeroing out memory sets pointers to `NULL`, and every implementation I've seen for a long time does define `NULL` as zero. – Andrew Henle Sep 25 '15 at 10:05
  • No, *you* do not understand the standard. The **null pointer *constant*** is an expression in C code (`0` or `(void*) 0)`). The **null pointer** is the machine representation. Everything that you've mentioned in your comments specifically refers to "the null pointer", not "the null pointer constant". In particular "Some implementations naturally represent null pointers by ... nonzero bit patterns" (note *null pointer*, not *null pointer constant*). "Any two null pointers shall compare equal": There may be different null pointers for different types, not different *null pointer constants*. – jamesdlin Sep 25 '15 at 17:03
  • See http://www.c-faq.com/null/null2.html and http://www.c-faq.com/sx1/index.html#null%20pointer%20constant – jamesdlin Sep 25 '15 at 17:05
  • Now, I will agree that the language of the standard does seem to *permit* the existence of other null pointer constants. Perhaps some implementation could choose to convert `(void*) -1` to a null pointer too and still be compliant. So you would be correct that `NULL` could be defined to something else. However, that has no bearing on the use of `memset`, because the conversion between the null pointer constant to a null pointer would not happen. – jamesdlin Sep 25 '15 at 17:16