3

Initializing an array of pointers to structs in C can be done using compound literals.

typedef struct {
int a;
int b;
} s;

In C:

s *ptrArray[] = {
    &(s){
        .a = 1,
        .b = 2
    },
    &(s){
        .a = 4,
        .b = 5
    }
};

How can this be done in C++?

I have also seen the difference in initializing structs in C++ not using compound statements:

s s1 = { a: 7, b: 8 };
einpoklum
  • 118,144
  • 57
  • 340
  • 684
vg34
  • 91
  • 2
  • 10
  • Read up on [containers](http://en.cppreference.com/w/cpp/container), [smart pointers](http://en.cppreference.com/w/cpp/memory/shared_ptr), [list initialization](http://en.cppreference.com/w/cpp/language/list_initialization) and [aggregate initialization](http://en.cppreference.com/w/cpp/language/aggregate_initialization). C and C++ are two completely different languages. – Ron Mar 01 '18 at 14:51
  • 4
    Why do you want to use pointers for that? Why don't you want to use `std::vector s1 = {{1,2}, {4,5}};`? – t.niese Mar 01 '18 at 14:52
  • Arrays, pointers, and arrays of pointers in C++ are best avoided. – n. m. could be an AI Mar 01 '18 at 15:04
  • @n.m.: That requires some link as a reference or an explanation. – einpoklum Mar 01 '18 at 15:06
  • @einpoklum These are very low level constructs, akin to goto. Not much to explain here. – n. m. could be an AI Mar 01 '18 at 15:10
  • @n.m.: Many people, who are not familiar with modern C++, will be very surprised to hear they're supposed to avoid low-level constructs. Also, these are not akin to goto, in that you would not want to use a higher-abstraction version of goto. – einpoklum Mar 01 '18 at 15:12
  • @einpoklum if, switches, loops and functions are more safely packaged versions of goto, I think most people would use them. Likewise safer packaged versions of pointers and arrays exist, one should prefer them, even for the high price of having to learn modern C++ practices. – n. m. could be an AI Mar 01 '18 at 15:18
  • @n.m.: 1. Your first comment did not say "I think it best to avoid them", it said "are best avoided". 2. If you refer to something like the C++ coding gudelines, you'd be able to state what the _community_ thinks. – einpoklum Mar 01 '18 at 15:43
  • @einpoklum when I'm convinced X is true, I say "X" not "I think perhaps you should consider X but this is just my insignificant opinion". Because it's not. – n. m. could be an AI Mar 01 '18 at 16:16
  • 1
    @n.m.: I think you should have a more pedagogical approach to comments on newbie questions. – einpoklum Mar 01 '18 at 16:24

2 Answers2

2

First - initializing anything to the address of a temporary value seems extremely fishy, in C as well. Are you sure that's valid? Hmmm. Anyway, a C++ compiler will really not let you do that.

As for the your designated (named-field) initialization C++ line - it's actually non-standard, it's a GNU C++ extension, and you can't rely on it.

You could do this:

struct s { int a, b; };

int main() {
    s data[] = { { 1, 2 }, { 4, 5 } };
    // instead of ptrArray[i], use &(data[i])
}   

This compiles just fine. But - a more C++'ish version of this code would be:

#include <array>

struct s { int a, b; };

int main() {
    std::array<s, 2> data { s{ 1, 2 }, s{ 4, 5 } };
    // instead of ptrArray[i], use &(data[i]),
    // or use iterators, or ranged for loops
}   

Why would you want to use std::array? Here's one explanation of the benefits. Actually, you could do slightly better and repeat yourself less with:

int main() {
    auto data = make_array(s{ 1, 2 }, s{ 4, 5 });
    // instead of ptrArray[i], use &(data[i]),
    // or use iterators, or ranged for loops
}   

The make_array function is taken from here; you also have std::experimental::make_array(), but that's not standardized yet.

If you want to add or remove elements from data at run-time, you might switch to using std::vector:

#include <vector>

struct s { int a, b; };

int main() {
    std::vector<s> data { s{ 1, 2 }, s{ 4, 5 } };
    // instead of ptrArray[i], use &(data[i]),
    // or use iterators, or ranged for loops
}   
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • Compound literals in C have the lifetime of the surrounding block as if they were local variables. They don't have the lifetime of C++ temporaries. Though it's worth noting that the GNU C++ extension which adds compound literals uses C++ temporary lifetime. – Candy Gumdrop Mar 01 '18 at 15:07
  • @einpoklum yes sorry for the confusion. – n. m. could be an AI Mar 01 '18 at 15:28
  • @CandyGumdrop: But Is the compound literal the initializer of ptrArray, or are the initializers of the pointers also blessed with the entire program's lifetime? – einpoklum Mar 01 '18 at 16:28
  • @einpoklum Each `(type) { initializer }` (C99 compound literal) expression effectively creates a new anonymous local variable (or global variable if created at file scope), so taking the address of one like `int *x = &(int) {123};` is perfectly safe and would be equivalent to `int foo = 123; int *x = &foo;`. The OP's example is equivalent to initializing an array with some addresses of local variables, which can be a perfectly valid thing to do. – Candy Gumdrop Mar 02 '18 at 10:08
1

The reason your initialize was failing is you were attempting to initialize the array of pointers to struct to the address of numeric literal constants. The same as:

#define A 5
int b = &A;    /* NOT HAPPENING */

(you can't take the address of 5)

You can solve your problem by simply initializing an array of s instead of an array of pointers to s, e.g.:

    s ptrarr[] = { {1, 2}, {4, 5} };

With that change, your array will initialize fine, e.g.

#include <iostream>

typedef struct {
    int a;
    int b;
} s;

int main (void) {

    s ptrarr[] = { {1, 2}, {4, 5} };
    int cnt = 0;

    for (auto& i : ptrarr)
        std::cout << "ptrarr[" << cnt++ << "] : " << i.a << ", " << i.b << "\n";

}

Example Use/Output

$ ./bin/ptrarrystruct
ptrarr[0] : 1, 2
ptrarr[1] : 4, 5
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Where is the OP taking the address of numeric literal constants? C99 compound literals evaluate to lvalues with a lifetime of the enclosing scope. The OP's example is completely valid C, just using a language feature which doesn't exist in C++. – Candy Gumdrop Mar 01 '18 at 15:27
  • The numeric intializers are not a *compound literals* (the cast is omitted). Without the cast, you are attempting to initialize the address of `{ {1,2}, {4,5} }`, you can't do that in C or C++. `"initialization makes pointer from integer without a cast"` – David C. Rankin Mar 01 '18 at 15:33