8

Please consider a heap-allocated array of pointers like this:

Thing** array = malloc(sizeof(Thing*) * array_length);

How can I set all of its members to NULL?

I can of course do this:

for (int i = 0; i < array_length; i++) {
    array[i] = NULL;
}

But I'm wondering if there's a standard function for this.

SO mainly mentions memset, however I'm not sure it is guaranteed to work on all platforms for this usecase, since as far as I understand it walks the array byte by byte, and not element by element.

Also more generally, what is the standard way to set all values in an array of any type T, which can be of any size, to some particular value?

Aviv Cohn
  • 15,543
  • 25
  • 68
  • 131
  • 5
    What's wrong with `calloc()`? – Sourav Ghosh Nov 22 '19 at 11:03
  • 2
    @SouravGhosh The fact that it *allocates*? ;-) I’m assuming OP wants to re-zero a preexisting array, though it’s not clear why that would be useful. And even `calloc` has the same conceptual problem as `memset` here, which is that `NULL` doesn’t necessarily correspond to an all-zero bit pattern. Though in practice the most code implicitly assumes that it does. – Konrad Rudolph Nov 22 '19 at 11:05
  • 5
    What's wrong with `memset(array_length, 0, sizeof array_length);`? But the example loop makes no sense anyway. Did you mean `array[i] = NULL;`? – Weather Vane Nov 22 '19 at 11:08
  • @KonradRudolph Pre-existing and heap-allocated...which environment? – Sourav Ghosh Nov 22 '19 at 11:09
  • 1
    @SouravGhosh I don’t understand the question. – Konrad Rudolph Nov 22 '19 at 11:09
  • @WeatherVane See my comment. – Konrad Rudolph Nov 22 '19 at 11:10
  • @SouravGhosh Doesn't matter. It may be heap-allocated or not (that's a different question and wasn't necessarry to be included in the question), but it might have been allocated on a completely different level as we are and our only job is it to clear the array. – glglgl Nov 22 '19 at 11:32
  • @M.M Please see my edit. – Aviv Cohn Nov 22 '19 at 11:58

4 Answers4

5

Your method is fine

I cannot think of any better way to to this than what you already are doing. It's short and clear. It's absolutely nothing wrong with it, and I would not even consider something else.

If it is to save a few lines you can use one of these:

  • for (size_t i = 0; i < array_length; i++) // No braces
        array[i] = NULL;
    
  • for (size_t i = 0; i < array_length; i++) array[i] = NULL; // Single

  • for (size_t i = 0; i < array_length; i++) { array[i] = NULL; } // With braces

But I want to point out that some people have strong opinions about this. I often skip braces for single statement loops. If you use any of these techniques, be prepared for complaints.

Unsafe alternatives

You could use memset, and it will work most of the times. However, the C standard does not require null pointers to be all zeros. Granted, the risk of a failure because of this is minuscule, but why take the risk when the gain is nearly zero? If you ask me, a loop is clearer, because your goal IS to set the pointers to NULL. The loop clearly communicates this.

There are historical examples with non-zero NULL machines: When was the NULL macro not 0?

However, do note that even though NULL is not required to be all zero bits, it IS guaranteed to evaluate to false when used in a Boolean expression. So you can safely use if(!ptr) to check if a call to malloc succeeded or not. I explained it in this answer Is NULL always false?

klutt
  • 30,332
  • 17
  • 55
  • 95
  • 1
    I actually have never seen an implementation in which `NULL` wasn't 0. Ok, it's wrong basing an answer only on experience, but definitions such as `#define NULL (1)` or `#define NULL (0xFFFFFFFF)` sound really strange to me. – Roberto Caboni Nov 22 '19 at 12:36
  • @Cubo78 See link – klutt Nov 22 '19 at 12:39
  • My goas wasn't mistrusting you. Anyway, thanks: it is very interesting ( and scaring)(and crazy). BTW, with non-zero NULLs, initialization of pointers with memset is not the worst practice: think about all pointer checks done with something like `if(pointer)` instead of `if(pointer != NULL)` – Roberto Caboni Nov 22 '19 at 12:45
  • @Cubo78 I did not interpret it as mistrusting. It's a valid objection. But if I'm not mistaken, the standard guarantees `if(NULL)` to be evaluated as false. – klutt Nov 22 '19 at 12:47
  • @Cubo78 `if (ptr)` and `if (! ptr)` are explicitly valid ways of checking for null pointers, as are `if (ptr == NULL)` and `if (ptr == 0)`! A conforming compiler must detect and translate them such that the correct check is performed. However, for instance `int x = (int) NULL; if ((int) ptr == x)` is *not* a valid check. – Konrad Rudolph Nov 22 '19 at 13:00
  • @KonradRudolph I also believe it's that way, but I could not find it in the standard. Do you know where? I want to add it to the answer. – klutt Nov 22 '19 at 13:50
  • @klutt Honestly I can’t find it either. http://c-faq.com/null/ptrtest.html references lots of sections that don’t exist in the current ISO C standard, and I don’t know what edition they refer to. – Konrad Rudolph Nov 22 '19 at 15:01
  • @KonradRudolph Yeah, that's what I also found, but it's not something I would call a reliable source. – klutt Nov 22 '19 at 15:03
  • @KonradRudolph Found it. https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3 – klutt Nov 22 '19 at 15:13
  • @klutt I’m sure that implies something but it’s not obvious that this implies that a null pointer, evaluated in a boolean context, evaluates to the integer value 0. Note that we’re not comparing to any other pointer here, neither explicitly nor implicitly. – Konrad Rudolph Nov 22 '19 at 15:23
  • @KonradRudolph https://stackoverflow.com/a/58997179/6699433 Please comment there – klutt Nov 22 '19 at 15:26
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/202907/discussion-between-konrad-rudolph-and-klutt). – Konrad Rudolph Nov 22 '19 at 16:01
  • You might want to use `size_t` instead of `int` for `i`. – chqrlie Nov 23 '19 at 10:14
1

The standard function for this is memset. Theoretically, it's not 100% portable, but in practice it's extremely unlikely that it should cause any real problems and it is widely used.

Instead of

for (int i = 0; i < array_length; i++) {
    array[i] = NULL;
}

you just can use this:

memset(array, 0, array_length * sizeof(array[0]));

The third parameter of memset is the length in bytes of the memory to be set to zero. Therefore ylu need to multiply the array length by the size of one array element which is sizeof(array[0]).

Now it is true that NULL can be something else than a value with all bits set to 0, in such an implementation memset won't work for setting all pointers to NULL. But this is rather theoretic, In the last 30 years I've never seen an implementation where NULL was different from a value with all bits set to 0.

As for your second question:

Also more generally, what is the standard way to set all values in an array of any type T, which can be of any size, to some particular value?

No, there is no such thing in C, you need to do it with a loop. In C++ you can do stuff like this.

klutt
  • 30,332
  • 17
  • 55
  • 95
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • 3
    I am pretty sure that this is in fact invalid C code, even though it’s so frequent as to be idiomatic. But OP explicitly asked about a *valid* way. **Null pointers are not guaranteed to be all-zero bits.** See [the C FAQ](http://c-faq.com/null/machexamp.html). Note that setting a pointer to zero via `p = NULL` is well-defined (and so is comparison via `p == NULL` or `p == 0`), but the resulting value of `p` is not necessarily 0 bytes. – Konrad Rudolph Nov 22 '19 at 11:25
  • Wouldn't it then be enough to write `memset(array, NULL, array_length * sizeof(array[0]));`? – Pedro Isaaco Nov 22 '19 at 11:28
  • @Piters no, because the second parameter of `memset` is a char (or a byte). – Jabberwocky Nov 22 '19 at 11:30
  • 4
    @Piters No. `NULL` still has the value `0` (or `(void*)0`), and furthermore will be passed to the function as an integer. It doesn’t magically cause the `memset` function to assign the correct bit pattern. – Konrad Rudolph Nov 22 '19 at 11:31
  • @KonradRudolph It is as valid as taking a `calloc()`ed array and assuming it is initialized with `NULL` pointers. – glglgl Nov 22 '19 at 11:33
  • Ok thanks, got you both :) But referring to https://stackoverflow.com/questions/1296843/what-is-the-difference-between-null-0-and-0 I could just use `'\0'` instead, couldn't I? Because "`\0` is defined to be a null character - that is a character with all bits set to zero." – Pedro Isaaco Nov 22 '19 at 11:33
  • @glglgl and an uninitialised static array, which defaults to `0` values. – Weather Vane Nov 22 '19 at 11:35
  • @glglgl Correct. Which is therefore *also* in fact invalid. – Konrad Rudolph Nov 22 '19 at 11:36
  • @Piters No, what makes you think so? `'\0'` and 0 are identical in C, and the whole point is that a null pointer is *not* necessarily all-zero. – Konrad Rudolph Nov 22 '19 at 11:38
1

Initializing the array with an explicit loop is the only standard conformant approach that is guaranteed to work correctly on all platforms. Good compilers should be able to convert the loop into whatever is more efficient on the target platform for zero initialization if indeed null pointers are represented with all bits zero there.

If you can assume that all-bits zero is a correct representation of null pointers, which is almost always the case but not guaranteed by the C Standard, you can use a call to memset or more efficiently, allocate the array with calloc():

Thing **array = calloc(array_length, sizeof(*array)); // initialize to all bits zero

This can be more efficient than both the call to memset and the explicit loop as calloc() may in some cases assume the memory it gets from the OS to already be zero initialized, as is the case of newly mapped blocks for security reasons.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
0

I think, this should work.

int *arr[10] = {0}; // arr is array of size 10, where each element is pointer to an int.

This will initialize every element of the array to 0.

This is also guaranteed to work on all the platforms.

Siddharth
  • 321
  • 1
  • 5
  • 1
    I think the question was about dynamically allocated arrays – M.M Nov 22 '19 at 11:42
  • 1
    @M.M Technically, it's not an array if it's dynamically allocated. – klutt Nov 22 '19 at 11:43
  • 1
    @klutt No. `int *pi = malloc(5 * *pi);` allocates an *array of five integers*. Same for `int *pi = calloc(5, sizeof *pi);`. The C standard literally uses that terminology. – Konrad Rudolph Nov 22 '19 at 12:50
  • @KonradRudolph Please give a specific chapter that uses that terminology or an exact quote. – klutt Nov 22 '19 at 12:55
  • @KonradRudolph I just found it myself here https://port70.net/~nsz/c/c11/n1570.html#7.22.3.2 and I'm convinced now. – klutt Nov 22 '19 at 12:57