0

I haven't written C++ for over 25 years and evidently I've forgotten a lot, and the compilers are now far more strict than they used to be. Consequently, I'm struggling and failing to create a dynamically allocated array of pointers to arrays of 8 unsigned char.

I believe I need a single variable that's a pointer. I expect that variable to be assigned at runtime to point at a dynamically allocated array of pointers. Each of the resulting pointers would then be assigned to point to an array of 8 unsigned char.

I think that the variable declaration for my array should be:

int (**arr)[8];

but I'm failing to work out how to allocate the storage.

For the first step I have tried a number of variations on this idea:

arr = new (int*[8])[4];

but all have been rejected as type incompatible, or flat out syntax errors. In desperation, I also tried:

arr = malloc(4 * sizeof(void *));

which was rejected with "cannot convert ‘void*’ to ‘int (**)[8]’" so I tried a cast:

arr = (int**[8])malloc(4 * sizeof(void *));

Which still failed.

EDIT: In light of comments so far, first, I'm writing in C++, for an ESP32 device, and have now progressed to these efforts (still failing)

arr = new (int(*)[8])[4]; // array bound forbidden after parenthesized type

and

arr = new (int(*)[])[4]; // cannot convert ‘int (**)[]’ to ‘int (**)[8]’

It's clear from the comments that "I need to relearn a ton of stuff", and even "this isn't the best way of doing it", but for now I would like to just get the job done. Can anyone simply tell me a) if my variable declaration is sound, and b) how do I allocate the initial array of pointers?

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
Toby Eggitt
  • 1,806
  • 19
  • 24
  • 1
    `int (**)[8]` and `int **[8]` are very different. The first is a pointer to a pointer to an array of 8 `int`. The latter is an array of 8 pointers to pointers to `int`. The former groups as `(*(*))[8]`. The latter groups as `*(*([8]))`. – Tom Karzes Apr 23 '22 at 22:22
  • 2
    C or C++? The way you would do this is radically different, depending on which language you choose (and much easier in C++). – Paul Sanders Apr 23 '22 at 22:23
  • And if you're thinking of doing this (or whatever else) in C++ then you should read one of [these](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). Guesswork won't get you there, it's a complicated business. – Paul Sanders Apr 23 '22 at 22:25
  • In C you don't need to cast the return from `malloc` like you do. In C++ you very rarely use `malloc`. – Ted Lyngmo Apr 23 '22 at 22:25
  • 2
    25 years ago C++ was a very different language. You'll find today you only use malloc for extremely low-level plumbing, use `new` only slightly more often, and use a lot of container and smart pointer classes from the Standard Library. You probably won't need an introductory book, but you will definitely need something o get you back up to speed. – user4581301 Apr 23 '22 at 22:29
  • 2
    _so I tried a cast_ And so many have perished doing that. – Paul Sanders Apr 23 '22 at 22:34
  • Do not tag C for C++ questions. – Eric Postpischil Apr 23 '22 at 22:51
  • 1
    It looks like you want `int(*arr)[4] = new int[8][4];` (or simply `auto arr = new int[8][4];`) or perhaps you want `int[4][8]`, it's hard to tell. But you could also wrap a `std::vector arr;` in a class and add `int& operator()(size_t y, size_t x) { return arr[y*x_size + x]; }` – Ted Lyngmo Apr 23 '22 at 22:52
  • @TedLyngmo would that not create a two-dimensional array, rather than an array of pointers to one dimensional arrays? I might be able to make my project work that way, but for the record, is it not very different? (It certainly was 25 years ago!! :) ) – Toby Eggitt Apr 23 '22 at 23:13
  • @TobyEggitt but you're trying to exactly that! I think question set incorrectly, you start from some middleware assumption about what you have to do syntactically. While you have to start with idea what kind of memory model or access model you need. Anything declared as some kind of pointer structure wouldn't contain two dimensional continuous array - subarrays will be not contiguous. Actually, simplest way to allocate a continuous two-dimensional array dynamically in C++ is to create a class that allocates a single-dimensional array that encompasses all elements and redefine `operator[]`. – Swift - Friday Pie Apr 23 '22 at 23:26
  • Or better `operator()`. Redefining `operator[]` requires declaring some type that acts as a view for a subarray and `operator[]` for it as well. – Swift - Friday Pie Apr 23 '22 at 23:28
  • @Swift-FridayPie that's precisely my point: I do *not* want a single contiguous structure, I want an array of pointers to individual arrays. Partly because I want to understand what syntax works for this. It's possible I might be able to redesign my plans to work with a two dimensional array, but then I won't have learned anything, and sometimes, the learning is as important as getting the job done :) – Toby Eggitt Apr 24 '22 at 01:29

2 Answers2

2

OK, since I don't like the other answer here (because I think it's leading you in the wrong direction), I will try to persuade you to use modern C++ idioms instead. As I have said more than once in the comments, this will pay off in spades once you master it.

First up, your question contradicts itself. First you say this:

[...] Each of the resulting pointers would then be assigned to point to an array of 8 unsigned char.

But then you say this:

I think that the variable declaration for my array should be:

int (**arr)[8];

So what do you want? Some sort of array of int's or an array of arrays of 8 unsigned char's? I'm going to go with the latter since that is what you said your goal is. If that's not in fact what you want, please clarify your question - concentrating on what data structure it is you want to represent, rather than how you think it should be achieved - and I will update this answer accordingly. Like I said (although I have now deleted the comment because I thought it was a little unkind), XY problem. And please forget about pointers. That's not the way to solve this problem (whatever it is, or any other, pretty much).

So, in modern C++, this is super-easy. For the sake of argument, let's suppose you want a data structure representing a fixed number of arrays (4, say) of unsigned chars, each array of unsigned chars being 8 bytes long. Well that's trivial, you can make use of what has to be the simplest STL container - std::array - and simply say:

// Define your types once ('using' is a sort of supercharged typedef) and use them everywhere
using my_array_element_type = std::array <unsigned char, 8>;
using my_array_type = std::array <my_array_element_type, 4>;
my_array_type my_array { };         // { } zero-initialises the array (I'm playing it safe here)

And then, to access an individual element in the array, you might do:

auto c = my_array [3] [5]; // 'auto' 'deduces' to unsigned char here

Now this looks lot like a 2-dimensional C-style array allocated on the stack, and indeed that's exactly what it is, but there are some important differences.

Given the above you can, for example, do this kind of thing:

my_array_type make_array ()
{
    my_array_type a { };
    // stuff things into the array, perhaps
    return a;
}

and then, at the point you want the array to come into existence, you can do:

auto my_array = make_array ();

And you can also write functions like this:

void use_array_read_only (const my_array_type &a)    // passed by reference, so not copied
{
    // .. do things with a (read-only)
}

and this:

void use_array_read_write (my_array_type &a)         // ditto
{
    // .. do things with a (read-write)
}

Note that these functions know all about the type of the parameter being passed to them, including how many elements it contains (so no need to pass additional one-day-you're bound to get-it-wrong parameters for these), and you can't say that about C-style arrays. The fact that these 'decay' to a pointer is notorious, everybody hates it. Even working out the type of the parameter for 2D arrays is a challenge which I personally am not smart enough to figure out unless I absolutely have to.

Being a stack-based variable, the lifetime of my_array is limited to the scope in which it is defined. I can only guess at whether this is an issue for you, but I'm sure you can cope with that. Also, since they're allocated on the stack, std::arrays must not be too big.

And if you want a data structure which can be resized, or is too large to fit comfortably on the stack, you can use std::vector instead. std::vector's API is in many ways a superset of that offered by std::array, so, for many purposes, it can be used in much the same way, once populated.


Right, tl;dr:

  • Forget, basically, all the C that you know; it will just lead you astray. In particular, forget that pointers and C-style arrays exist. (For now anyway. Pointers do have their place, but you need to learn about 'smart pointers' to use them safely and that's for another day).

  • Forget all about malloc, free, new and delete. You won't need them. Ever.

  • Learn to make basic use of the three containers that are the workhorses of the STL: std::array, std::vector and std::string. They're not hard to use and if you run into something you don't know how to do or don't understand, you can always ask a question here on SO. That's what the site is for.

  • Learn what references (and const references) are. You will want to use them as function parameters to avoid copying a container every time you pass it to a function. It will also allow you to modify that container inside the function, if that's what you want to do.

  • Buy yourself a suitable book. You need it now, not next week or next month or next year!

  • Be aware that cppreference exists. You'll find it tough at first but it's a really useful site and things will get easier. Also, when starting out, you might find (cplusplus.com)[https://www.cplusplus.com/] more accessible, but cppreference is more comprehensive and more reliable.

  • When you want to try out something new, you can use one of the many online compilers that are out there. I find them invaluable and personally favour Wandbox. The link points to a simple demo of std::array for you which you might like to play with.

That's it. Best of luck! Mine's a beer. If you get stuck in, you'll find a lot of people willing to help you here on SO. If you stick with outdated C idioms, not so much. We see a lot of that here and it's kinda depressing.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
0

I believe you want to get to know ** double pointer and how to use them.

// explanation of what double pointer is.

    int **arr1; //double pointer: is a pointer to point to pointer
 
    // example
    int* p = new int(1);
    arr1 = &p; // points to pointer int*, note the "&" affront which returns the memory address of p.
    arr1 = new int*[10]; // points to int*, "new" returns the memory address of newly allocated memory space.


// harder example, how to point to a double array [][]
    int num_rows = 10;  
    arr1 = new int*[num_rows];

    for (int i = 0; i < num_rows; ++i)
    {

        int num_cols = i + 1; // the column length can be different for each row
        arr1[i] = new int[num_cols];

        for (int j = 0; j < num_cols; ++j)
            arr1[i][j] = i;

    }

note that ** points not only those rows with fixed length, it can point to unequal length of data rows as shown in example. pay care to track the row lengths or just use double array.

  • Aha, so the bottom line is that my error was trying to specify the size of the secondary arrays referred to by the primary arrays. C++ can't keep track of that detail (and indeed has no need to, I guess), so I was adding a level of information that the type system wasn't ready or willing to handle. Thanks, this solved the issue perfectly. – Toby Eggitt Apr 24 '22 at 03:27
  • I don't think this answer is appropriate. We should be directing the OP towards [`std::array`](https://en.cppreference.com/w/cpp/container/array) or [`std::vector`](https://en.cppreference.com/w/cpp/container/vector). the sooner he starts using these, the happier he will be. @toby please take note and do some reading. I wouldn't be saying this if I didn't think it was in your best interests. – Paul Sanders Apr 24 '22 at 13:05
  • @PaulSanders trust that I will investigate these libraries. – Toby Eggitt Apr 24 '22 at 13:25
  • @TobyEggitt OK, good. I will try to find the time today to post a 'modern C++' answer, to show you how easy this can be. – Paul Sanders Apr 24 '22 at 13:44