1

I'm working in a C++ project where I'm trying to create a struct which posses an array, whose size will be determined when the method is called. This, though, is giving me the famous error "array bound is not an integer constant" (using GCC and Qt).

I did some research in StackOverflow and other places but couldn't find any solution for my particular situation: either the size of the array was clearly non-constant, or else the problem didn't appear JUST when the array is located inside a struct, not outside of it.

To give the code I used for testing:

void QuantitySelectorCenterView::accepted()
{
    const int numItems = modelSelectedList->rowCount();

    uchar selectedItemsX[numItems];

    struct PQDataRequest
    {
        re8k_ict_header header;
        re8k_ict_physical_quantity quantity;

        uchar selectedItems[numItems];
    };

    struct PQDataRequest2
    {
        re8k_ict_header header;
        re8k_ict_physical_quantity quantity;

        uchar selectedItems[10];
    };
}

In the following code, the "rowCount()" value of the modelSelectedList is defined at runtime, depending on the configuration the user sets. When he press the OK button, "accepted" is called. In a first moment, the compiler disliked the rowCount() returned value since it was a common int; I put its value in a const int, "numItems". Don't know if this actually changes something, but the declaration of an array (selectedItemsX) didn't returned any error. I'ld expect, therefore, that I could use such code. But when I created the struct "PQDataRequest", the compiler gave that error for the "selectedItems" array inside it. The SAME array, now having problems for non-const size! And the second struct shown in the code above don't present any errors.

So why can I declare and use an array such as selectedItemsX outside a struct declaration, but I can't use an exactly equal array inside a struct? And how may I overcome this problem? Notice that I can't use a variable-size container such as vector because the same algorithm needs to be implemented later in a C code in a similar fashion, and the usage of a pointer to an array inside the struct is problematic since I'll need to use sizeof() in the struct later, and I can only know the size of the array at runtime when accepted() is called.

Thanks for any help,

Momergil

Momergil
  • 2,213
  • 5
  • 29
  • 59
  • You should tell your compiler to be more pedantic. `-Wall -Wextra -pedantic` at least. Arrays with run-time bounds are not supported in standard C++ (yet) and they're typically dangerous and better avoided in favour of heap-based allocation like `std::vector`. – dyp Jun 04 '14 at 21:04
  • 1
    " the same algorithm needs to be implemented later in a C code" - why don't you implement it in C, and make your C++ program link to that? – M.M Jun 04 '14 at 21:35
  • @MattMcNabb well, it wouldn't make much of a difference: I would still have to learn how to implement it ;) – Momergil Jun 05 '14 at 12:03
  • Yes but you can use *flexible array member* in C. Although malloc works just as well and is arguably better. – M.M Jun 05 '14 at 12:06
  • @dyp thanks for the tip about -pendantic; I had no idea this thing exists :) – Momergil Jun 05 '14 at 13:49

6 Answers6

2

uchar selectedItemsX[numItems]; This is a VLA (variable length array, and is at-best supported by non-standard extension through some C++ implementations.

Making numItems a const has nothing to do with getting by that fact. All that does is guarantee once numItems is initialized it is not mutable. It has to be a compile-time const expression (and it isn't in this case).

Use std::vector<uchar> selectedItemsX, construction-initialized to the appropriate magnitude if you want universal C++ support (and consider all the whiz-bang features of std::vector<> as a bonus). You said, "Notice that I can't use a variable-size container such as vector because the same algorithm needs to be implemented later in a C code in a similar fashion." C is not C++. The mechanism you're using in C utilizes a flexible array member, and no such nicety exists in standard C++

Community
  • 1
  • 1
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
2

If you are targeting C then write C - you could compile with C++ compiler but choosing to compile as C at this point will avoid hassle down the line.

Your problem occurs because the array dimension must be a compile time constant i.e known and fixed when the code is compiled. As WhozCraig points out const only ensure that once the value of numItems is set (at run time) it can not be changed.

There are 3 solutions to your problem:

1) Use a compile time upper bound constant for the array size:

static const int MaxSelectedItems = 128; 
typedef struct {
    re8k_ict_header header;
    re8k_ict_physical_quantity quantity;
    uchar selectedItems[MaxSelectedItems];
} PQDataRequest;

MaxSelectedItems array index must never be exceeded. i.e code that sets selectedItems should check that index < MaxSelectedItems (Note that with the other dynamic solutions below this check is imposed also so its not really a downside). If the code is C becauses its going to run embedded this is the route to go - you don't generally start mallocing and freeing mem in embedded enviroments unless there is a real need. The only real downside here is it uses mroe memory - normally this not a problem (even embedded) and is prefered over the potential problems introduced by dynamic memory allocation.

2) Use a pointer field:

typedef struct {
    re8k_ict_header header;
    re8k_ict_physical_quantity quantity;
    uchar* selectedItems;
} PQDataRequest; 

Here for each use of the struct you will have to malloc and free the selectedItems field.

int selectedItemsSize = 20;
PQDataRequest myDataRequest;
myDataRequest.selectedItems = malloc(sizeof(uchar)*selectedItemsSize;
// don't foreget to free myDataRequest.selectedItems when done!

Use this if you really MUST have dynamic allocation and you dont intend to use arrays of PQDataRequests (see below if you do). Not really recommeded unless strictly required because you have the hassle of allocating and more importantly freeing the selectedItems array for each use of the struct.

3) Use C99 Flexible array member:

 typedef struct {
        re8k_ict_header header;
        re8k_ict_physical_quantity quantity;
        uchar selectedItems[];
    } PQDataRequest;

This is recommended if you are planning to use and array of PQDataRequests. This method allows you to malloc (and latter free) memory for the WHOLE array:

int dataRequestArraySize = 5;
int selectedItemsSize = 20;
PQDataRequest* my_array = malloc((sizeof(PQDataRequest) + (selectedItemsSize*sizeof(uchar))) * dataRequestArraySize);

Instead of having to allocate and free the selectedItem for each PQDataRequest in the array.

In all of the above three cases you will also have track the count of selected items - most likely by including an int selectItemsCount field int the struct. In 1) this enables knowing which of the values in the array are valid (< than selectItemsCount) and which are not used (> selectItemsCount but < MaxSelectedItems). In 2 & 3 this count enables you to do a check that when ever you read or write the array that you are not referencing outside the dymamically allocated size.

Ricibob
  • 7,505
  • 5
  • 46
  • 65
  • thanks for the reply (certainly the most complete one). Could you please give an explanation for "you don't generally start mallocing and freeing mem in embedded enviroments unless there is a real need"? – Momergil Jun 05 '14 at 12:08
  • 1
    MISRA is a set of guidelines and rules for developing critical embedded systems. One of those guidelines/rules is don't do dynamic memory allocation. The issue is that knowing when to free dyn allocated memory is not always straight forward and a lot of bugs are related to failure to free dynamically allocated memory. – Ricibob Jun 05 '14 at 12:59
  • Well, I decided to select your answer as the best one (although you didn't exactly explained why the problem happened, going straightforward to solutions and, in that, you're both the most complete as the most clear in doing it). I'ld suggest you edit your post to include the explanation for why my problem occurred, specially taking into consideration the answers provided by the other users, so new readers may not have to read all the answers to have a full cover of the problem :) Thanks! – Momergil Jun 05 '14 at 13:54
1

The size of the struct is going to have to be determined at compile time, for the compiler to know how to work with the type. So, trying to use a dynamic value to declare an array in it will be illegal.

The array you've declared in the body of the function is created on the stack during execution, so it's allowed to have variable size.

kevintodisco
  • 5,061
  • 1
  • 22
  • 28
1

When making an array in c++, you need to know the size at compile_time. When this cannot be determined, it cannot create it.

A possible solution would be to create your array in the dynamic memory. I'd recommend checking out http://www.cplusplus.com/reference/new/operator%20new[]

This will allow you to do what you want, but you should remember to free your memory afterwards!

sizeof() shouldn't be a necessity, as you can just keep the amount of elements of the array in a seperate value and then multiply it with sizeof(array[0])

Pinna_be
  • 4,517
  • 2
  • 18
  • 34
  • 1
    Using the new-expression would be better than `operator new[]`. Although `std::vector` is better than both. – M.M Jun 04 '14 at 21:34
  • @MattMcNabb aren't those the same? And indeed, vector would be better, but Momergil specifically said he couldn't use that, as the problem would rise when he would port his code to c. This way it is easy to port to c. – Pinna_be Jun 04 '14 at 22:09
  • 1
    The *new-expression* is when you write `new Type[n]` in your code. `operator new` and `operator new[]` are functions which allocate memory. The new-expression calls `operator new` and then the constructor of the objects. – M.M Jun 04 '14 at 22:43
0

The problem is that the size of the first struct cannot be known at compile time and that's not legal. You can not have a dynamically sized struct, otherwise things like sizeof(struct PQDataRequest) could not be statically determined at compile time.

Sorry, but if you need a dynamically sized element inside of your struct you will have to leave it as a pointer then dynamically allocate an array at creation time.

caskey
  • 12,305
  • 2
  • 26
  • 27
0

and the usage of a pointer to an array inside the struct is problematic since I'll need to use sizeof() in the struct later

This is true, however, you could still use a pointer to an array and also keep a size_t that's set to numItems which can be used to determine the length of the array later in your code.

bstar55
  • 3,542
  • 3
  • 20
  • 24