1

So I was just playing around with Array pointers to see how they work, my example below allocates space for 2 array pointers to an array with 10 ints.

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

int main(void) {
    int (*p)[10] = malloc(sizeof(int[10]) * 2);

    for (int arrayI = 0; arrayI < 2; ++arrayI) {
        for (int i = 0; i < 10; ++i) {
            p[arrayI][i] = (arrayI+1) * i;
        }
    }

    for (int arrayI = 0; arrayI < 2; ++arrayI) {
        for (int i = 0; i < 10; ++i) {
            printf("%d\n", p[arrayI][i]);
        }
        printf("\n");
    }
}

This seems to work fine and gives me:

C:\Users\USERNAME\Desktop>gcc -Wall -Wextra --std=c18 a.c && a.exe
0
1
2
3
4
5
6
7
8
9

0
2
4
6
8
10
12
14
16
18

For my question, you rarely see code like this, if at all. Is there anything dangerous with doing things like this or is it just "bad code". And again, this is just me playing around with Array pointers.

Fredrik
  • 1,389
  • 1
  • 14
  • 32
  • 2
    *"you rarely see code like this, if at all"* - you clearly haven't read my code. When allocating a dynamic array of *fixed* arrays, this is *the* go-to way to do it. Only if you have intentions of picking individual rows for swapping, insertions, removals, etc of the superior dimension would you consider a pointer array instead. Unrelated, `calloc(2, sizeof *p)` or `malloc(2 * sizeof *p)` will also work, and arguably be less prone for errors if/when you change the fixed dimension (i.e. there is only one place to change it; the decl). – WhozCraig Dec 10 '20 at 08:38
  • @WhozCraig Yeah I was thinking this would be nice when allocationg a lot of arrays with the same size! :) – Fredrik Dec 10 '20 at 08:39
  • Even more *playful* would be: `int n = 7, q = 3, (*p)[n] = malloc(sizeof(int[n]) * q);` (and suitably changing the `2`s and `10`s in your loops for `n` and `q`). – Adrian Mole Dec 10 '20 at 08:40
  • @AdrianMole That code is UB since `n=7` isn't sequenced in relation to the other `n` access in the same initializer list. Not to be confused with `n = 7, q = 3, (*p)[n] = malloc(sizeof(int[n]) * q);` (comma operator) which is actually well-defined. – Lundin Dec 10 '20 at 08:53
  • Btw some related reading here: [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Lundin Dec 10 '20 at 08:55
  • Ask yourself how many allocs were required and how many frees? How many would have been required if you allocated a block of pointers first and then individual rows worth of `int`? See the benefit? – David C. Rankin Dec 10 '20 at 09:02
  • @DavidC.Rankin Sure do! – Fredrik Dec 10 '20 at 09:02
  • The *pointer-to-array* when you know the number of column elements (regardless whether the elements are simple `int` or other aggregate structs, etc...) is an efficient way to allocate the space and the single-allocation/single-free sure makes things easy `:)` – David C. Rankin Dec 10 '20 at 09:05
  • @Lundin Well, I did say *playful*. But you're right. In the code I checked, I actually had `n` and `q` defined on a previous line - so no UB. – Adrian Mole Dec 10 '20 at 09:06

2 Answers2

2

Is there anything dangerous with doing things like this or is it just "bad code".

No. This is actually a correct way of making a dynamic allocated 2D array.

Instead of

int (*p)[10] = malloc(sizeof(int[10]) * 2);

I prefer

int (*p)[10] = malloc(2 * sizeof *p);

As p is a pointer to an array of 10 ints, *p is an array of 10 ints.

Many prefer to have an explicit release of memory. It's not required as the program terminates anyway. It's opinion based but I would add:

free(p);
return 0;

at the end of the program.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • More to the core point of the second snipped, `Type *var = malloc(N * sizeof *var);` (or `Type *var = calloc(N, sizeof *var);` ) is fundamental, whether `Type` is an array type or not. The syntax with the delcaration parens is the only messy part to get used to with arrays. The rest is the same old same-old. – WhozCraig Dec 10 '20 at 08:43
2

my example below allocates space for 2 array pointers to an array with 10 ints

No, this is one array pointer int (*p)[10]. This gives 2 arrays: sizeof(int[10]) * 2.

Is there anything dangerous with doing things like this

The only danger is the obscure de-referencing syntax. The most correct way to write your example is this:

 int (*p)[2][10] = malloc(sizeof(int[2][10]));

or equivalent

 int (*p)[2][10] = malloc(sizeof *p);

But if you do that, you have to write the strange-looking (*p)[i][j] when de-referencing. Therefore we drop the left-most dimension, a "manual array decay", and get a int (*)[10] which is actually a pointer to the first element in a int[2][10] array. If you want, you could also rewrite the code as:

int (*arr)[2][10] = malloc(sizeof *arr);
int (*p)[10] = *arr;

Also, you should always free the allocated memory, since free will often result in a crash in case you have any lurking pointer corruption bugs somewhere in the program.

Lundin
  • 195,001
  • 40
  • 254
  • 396