2

I'm trying to define an instance of a struct, and am having particular trouble with setting this one variable. It's an array of char arrays.

Here is my struct in my header file...

struct widget_t {
    char *name;
    uint8_t numberOfNicknames;
    char *nicknames[];
};

And here's me attempting to set up the instance of the widget_t struct...

widget_t SomeWidget;

void setUpFunction () {
    SomeWidget.name = (char *)"Lawn Mower";
    SomeWidget.numberOfNicknames = 2;
    SomeWidget.nicknames = {
        "Choppie McGrasschopper",
        "Really Noisy"
    };
}

So, the error is happening when I try to put the nicknames into SomeWidget.nicknames. I'm not sure if I need to do something funky like I'm doing with name being a pointer...?

The tricky bit is that the number of nicknames is variable. So each instance will want to set up a different number of them.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
chrispitzer
  • 991
  • 1
  • 8
  • 26
  • Even though C++ does not have flexible array members, compilers that offer this as an extension pretty much follow the C semantics. Which means, this Q&A https://stackoverflow.com/q/8687671 is pretty much a duplicate, despite that tag mismatch. – StoryTeller - Unslander Monica Jan 16 '19 at 08:04

4 Answers4

1

The problem you have, is that c++ does not support variable arrays. Instead you will have to allocate memory dynamically using new or in your case new[].

First you need to change your data type to char**, almost equaliant to the one before. Then you can allocate as many strings you want like this nicknames = new char*[number_of_nicknames].

Important is that with this method you will have to delte your nicknames manually like this: delete[] nicknames;. The best way to accomplish this is using RAII (delete your nicknames in your deconstructor)

When you have dynamic strings then you would use the following structure

struct widget_t {
    // optional constructor to allocate nicknames

    ~widget_t()
    {
        for (int i = 0; i < numberOfNicknames; ++i)
        {
            char* nickname = nicknames[i];

            if (nickname)
                delete[] nickname;
        }

        delete[] nicknames;
    }

    char *name;
    uint8_t numberOfNicknames;
    char **nicknames = NULL;
};

and with constant string the next

struct widget_t {
    // optional constructor to allocate nicknames
    // allocate nicknames like
    // -> nicknames = new const char*[numberOfNicknames];

    ~widget_t()
    {
         if (nicknames) delete[] nicknames;
    }

    char *name;
    uint8_t numberOfNicknames;
    const char **nicknames = NULL;
};
Phisn
  • 861
  • 8
  • 18
  • @duong_dajgja changed, I think RAII would be suited for this purpose. – Phisn Jan 16 '19 at 09:26
  • Beware: this is not a VLA but an incomplete type. They are different animals and different semantics in C. But both are unspecified by C++ standard... – Serge Ballesta Jan 16 '19 at 09:47
0

What you are trying to do is to assign a string literal to a char * pointer -- that is bad (see more at How to get rid of deprecated conversion from string constant to ‘char*’ warnings in GCC?). Here is an possible approach:

#define MAX_NAME_LEN 128
#define MAX_NICK_NAMES 10

struct widget_t {
    char name[MAX_NAME_LEN];
    uint8_t numberOfNicknames;
    char nicknames[MAX_NICK_NAMES][MAX_NAME_LEN];
};

widget_t SomeWidget;

void setUpFunction () {
    strcpy(SomeWidget.name, "Lawn Mower");
    SomeWidget.numberOfNicknames = 2;
    strcpy(SomeWidget.nicknames[0], "Choppie McGrasschopper");
    strcpy(SomeWidget.nicknames[1], "Really Noisy");
}

Anyway, since you tag your question with C++ I would suggest you to use std::string and std::vector instead.

duong_dajgja
  • 4,196
  • 1
  • 38
  • 65
  • He also tagged his answer with arduino, so your last suggestion wont be possible. – Phisn Jan 16 '19 at 08:18
  • @Phins there are STL ports for arduino. – Dan M. Jan 16 '19 at 08:47
  • @DanM.but is not a good option https://arduino.stackexchange.com/questions/24790/is-the-c-stl-fully-supported-on-arduino, STL is optimized for a different platform. – Phisn Jan 16 '19 at 08:54
0

One option would be:

struct widget_t {
    char const *name;
    uint8_t numberOfNicknames;
    char const * const *nicknames;
};

static char const *mower_nicknames[] = { "Choppie", "Bob" };
widget_t SomeWidget = { "Lawn Mower", 2, mower_nicknames };

static char const *bus_nicknames[] = { "Wheels", "Go", "Round" };
widget_t OtherWidget = { "Bus", 3, bus_nicknames };

// no setup function needed
M.M
  • 138,810
  • 21
  • 208
  • 365
0

There are different problems here.

First your last member in of an incomplete type because it is an array of undeclared dimension. This is not allowed in C++ but most compilers allows it as an extension with same semantics as C. This is rather tricky to use anyway, because it can only be used with allocated structs, where you allocate memory for the struct itself and the incomplete array.

Next, you are trying to assign to a array. You cannot. Arrays are not first class objects in C++ (nor in C). You can initialize an array as a whole, but can only assign to an array element.

And last, you are assigning a C litteral string to a char *. This is bad, because the standard declares that litteral strings are const so using later the pointer to change a char would be Undefined Behaviour. It will work if you do not, but the pointers should at least be declared as const.

Here is how you could use all that:

widget_t* setUpFunction () {
    // allocates a widget_t with 2 slots in nicknames
    widget_t *someWidget = (widget_t *) malloc(sizeof(widget_t) + 2 * sizeof(char *));
    someWidget.name = (char *)"Lawn Mower";   // VERY DANGEROUS: pointer should be const
    someWidget.numberOfNicknames = 2;
    someWidget.nicknames[0] = (char *) "Choppie McGrasschopper"; // SAME DANGER
    someWidget.nicknames[1] = (char *) "Really Noisy"            // Still same danger
    return widget_t;
}

But all this is rather C-ish and should be avoided in C++. In addition, it still requires allocated memory, which may not be what you want for Arduino

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252