5

So I want to have a definition of the size of an array within a struct known at compile time. I would also like that number available as a variable for ease of use later. What I have is the following:

const int BANANA_ARRAY_SIZE = 10;

typedef struct
{
  char bananas[BANANA_ARRAY_SIZE];
  int some_other_stuff;
} banana_struct;

This functions fine. However, if I have it in a .h file that gets included in multiple places, the compiler complains about redefinition of BANANA_ARRAY_SIZE (which makes sense). So I need to declare it as an extern so multiple compilation units can know about it.

So, I now have

extern const int BANANA_ARRAY_SIZE;

typedef struct
{
  //.. same as before
} banana_struct;

and in an implementation file I have

const int BANANA_ARRAY_SIZE = 10;

But now the compiler won't let me define the struct anymore with complaints of

fields must have a constant size: variable length array in structure

Is there any way I can achieve what I want (have length of array stored in variable and used to define struct)?

Edit:

In response to the suggestions to instead use #defines, I'd rather not.

My question is about how to have this constant value stored in a variable and also used to set the length of an array in a struct. If you need further justification, assume I need a function that takes a pointer to an int. Can't take a reference of a #define.

Phildo
  • 986
  • 2
  • 20
  • 36
  • 1
    You can evidently only do this type of initialization in local scope in C, not global scope, which you are attempting. ***[ref](http://stackoverflow.com/a/11541168/645128)*** and in the same post: ***[ref2](http://stackoverflow.com/a/11542474/645128)*** – ryyker Jun 01 '15 at 21:22
  • Please read my updated comment, the last comment on my answer. It doesn't happen for me _gcc-5.1.0_. – Iharob Al Asimi Jun 01 '15 at 21:22
  • 3
    `#define BANANA_ARRAY_SIZE 10` `char bananas[ BANANA_ARRAY_SIZE ]; const size_t length = BANANA_ARRAY_SIZE;` – chux - Reinstate Monica Jun 01 '15 at 21:28
  • @ryyker: Tha t will not work in local scope either, as the VLA had to be at top-level of the block, not inside a struct (however, there is a way with a flex array member, but that would include some heavy stuff. – too honest for this site Jun 01 '15 at 23:07

4 Answers4

8

In C language const object is not a constant. You cannot use it to declare a non-VLA array. Your first declaration is not compilable in C language, which makes it unclear what you mean by "functions fine".

See here for more details: Shall I prefer constants over defines?

In your case you are restricted to either #define or enum constants. If you also want to create pointers to that value, you will have to additionally declare a const object with the same value, either globally or locally as needed. But you won't be able to use it in non-VLA array declarations. Instead, you will have to use the #define or enum constant. E.g.

// In .h file
#define BANANA_ARRAY_SIZE 10
extern const int BANANA_ARRAY_SIZE_OBJ;

// In .c file
const int BANANA_ARRAY_SIZE_OBJ = BANANA_ARRAY_SIZE;
Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • I wonder if gcc is not spec compliant with respect to that, as this: const int LEN = 12; char my_array[LEN]; int main(){ my_array[0] = 'a'; return 0; } compiles fine... Anyways, I guess that essentially "this can't be done according to spec" in c. Thanks for the info! – Phildo Jun 01 '15 at 21:37
  • @Phildo:`-std=c99` or `c11`? It's not the "spec", but the standard. – too honest for this site Jun 01 '15 at 21:59
  • @Phildo: That's definitely non-compliant in any version of C. Are you sure you are compiling in C mode and not in C++ mode? In C++ that would be legal. – AnT stands with Russia Jun 01 '15 at 22:04
  • @Olaf compiling the code in my earlier comment with - `gcc -std=c11 main.c -o a.out` works. ditto for `-std=c99`. – Phildo Jun 01 '15 at 22:32
  • 1
    @AnT: C99 ISO/IEC 9899:TC3 § 6.6 Constant Expressions ... ¶ 10 “An implementation may accept other forms of constant expressions”! But ¶ 6 says that an _integer constant expression_, used in array size, is a combination of integer/enum/char constants, `sizeof` and casts from float constants. § 6.7.5.2 ¶ 2: “If an identifier is .. an object with static storage duration, it shall not have a variable length array type.” ¶ 4: “If size is an _int~ const~ expr~_ and element type has known constant size, the type is not a variable length array type; otherwise, _(it is)_.” — so gcc goes too far!? – PJTraill Jun 01 '15 at 22:50
  • @PJTraill: At least gcc 4.8.2 not. Does not work with that. I just have no 5.1 at hand, but cannot find anything in its manual either. – too honest for this site Jun 01 '15 at 23:00
4

You can either use a enum or a #define

enum {
BANANA_ARRAY_SIZE = 10
};

or

#define BANANA_ARRAY_SIZE 10
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Mukesh MV
  • 61
  • 1
  • 5
0

You must use extern for that, so it's not redifined, and you will need to define it once in a single .c file.

Example

  1. header.h

    extern const int BANANA_ARRAY_SIZE;
    
  2. main.c1

    void banana(void); /* provide a prototype for `banana()' */
    /* A single definition */
    const int BANANA_ARRAY_SIZE = 10;
    
    int main(void)
     {
        banana();
        return 0;
     }
    

1This is for demonstration purpose, you don't just add the prototypes in real life, you must care about the source code layout and ensure that the prototype matches the function definition, etc.

  1. banana.c

    #include "header.h"
    #include <stdio.h>
    
    void banana(void)
     {
        printf("%d\n", BANANA_ARRAY_SIZE);
     }
    

Compiling with

gcc -Wall -Werror -o banana banana.c main.c    

when executed will print 10.

But I would recommend a macro instead, like

#define BANANA_ARRAY_SIZE 10

then you can even defined it at compile time like

gcc -DBANANA_ARRAY_SIZE=10 a.c b.c d.c -o executable

to prevent redefinition or no definition at all a common header file would contain

#ifndef BANANA_ARRAY_SIZE
#define BANANA_ARRAY_SIZE 10
#endif
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • How is this any different from what OP has? He explicitly mentioned he has used `extern` in the header file. The question is about defining the struct in the header file while keeping `BANANA_ARRAY_SIZE` definition in the .c – Filipe Gonçalves Jun 01 '15 at 21:16
  • I am aware of using extern. I even have that in my question. The question is "why can't I use the extern const value to define the length of the array?". Also, see my edit re: using #defines – Phildo Jun 01 '15 at 21:16
  • 1
    @Phildo I totally missed the question, since it seems a lot simpler, but that's an excelent question for which I don't have an answer yet. And, I am sorry but _gcc_ compiled an example I just wrote, so can't reproduce your problem, which compiler are you usign and which version? – Iharob Al Asimi Jun 01 '15 at 21:19
  • You cannot do as he wants in C with gcc. Even _if_ a compiler would accept this, it would be _implementation defined_ at best. – too honest for this site Jun 01 '15 at 23:10
0

According to C11 draft 6.6#10, "An implementation may accept other forms of constant expressions.".

Tried that with gcc 4.8.2:

static const size_t arr_size = 10;  // static does not change anything
static int arr[arr_size];           // dito

gcc -std=gnu11 -Wall test.c

test.c:6:12: error: variably modified ‘arr’ at file scope
static int arr[arr_size];

So that does not work (would have surprized me if it had). Even if id worked, it would be implemenation defined and might change even between two patches for the same compiler.

There is a clear reason for this not working, expecially if the const has global scope: If a module is compiled which includes the header with the declaration, the compiler has no way to knwo the actual value of that constant variable(!). So how would it reserve space for the array in .bss section or be able to check for ranges if initializers are given (and alloc space in .data section)? That would require intervention by the linker and would have much more impact on initializers, etc. I'm quite sure no compiler will accept this for the reasons given.


So, you have to resort to using #define:

"array.h":
#define MAX_ARR_LEN 10
extern const size_t max_arr_len;

"array.c":
const size_t max_arr_len = 10;

Note: use size_t for array bounds. That's what it is for (among others).

too honest for this site
  • 12,050
  • 4
  • 30
  • 52