1

Let's say I have two statically allocated global arrays:

const int foo[] = {1, 2, 3};
const int bar[] = {4, 5, 6, 7};

Later on the same compilation unit I'd like to declare an array that is also statically allocated and is the concatenation of foo and bar:

const int foo_and_bar[] = ... ?

Possible or not?

Of course, I could do something ugly with macros like...

#define FOO_CONTENTS 1, 2, 3
#define BAR_CONTENTS 4, 5, 6, 7

const int foo[] = {FOO_CONTENTS};
const int bar[] = {BAR_CONTENTS};

const int foo_and_bar[] = {FOO_CONTENTS, BAR_CONTENTS};

... but I'm looking for a supported idiom in the C language itself.

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • "really ugly" what's wrong with that elegant and extremely simple solution? – Alex Celeste Nov 06 '16 at 19:05
  • Perhaps I could have just said "ugly" - "really ugly" might be an exaggeration! It's ugly because every array declaration needs to be split up into a macro and declaration, rather than just expressing "this array should have the contents of these other two earlier declared variables". Reading the code is tougher because you need to decipher the macro and go find it in the file, etc. Certainly many other scenarios along these lines (putting statically allocated ints into arrays, etc) don't need preprocessor support. – BeeOnRope Nov 06 '16 at 19:09

4 Answers4

1

Strictly speaking I don't think you can do this statically in the unextended language (I'm sure tolerant compilers like GCC allow several options here though, taking advantage of the fact that their C++ frontends require this kind of thing). Two relevant standard quotes, emphases mine:

All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

(6.7.9 p4)

The array-subscript [] and member-access . and -> operators, the address & and indirection * unary operators, and pointer casts may be used in the creation of an address constant, but the value of an object shall not be accessed by use of these operators.

(6.6 p9)

In other words, while there are some legitimate uses for the [] operator at file scope, you explicitly aren't allowed to read the array's contents at that time. Therefore, to set the contents of one array to be equal to the contents of another, without simply duplicating the initializers (which is mechanically what the macro solution does), you must do some amount of work at runtime.

Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • Although I hoped for a "you can!" answer, this looks like about as definitive as it gets for "you can't". – BeeOnRope Nov 06 '16 at 19:50
1

Here is an alternative approach:


Since all three arrays are read-only, you may as well allocate only one of them:

const int foo_and_bar[] = {1,2,3,4,5,6,7};

Then, use additional variables indicating the index and size of each sub-array:

int fooIndex = 0, fooSize = 3;
int barIndex = 3, barSize = 4;
barak manos
  • 29,648
  • 10
  • 62
  • 114
  • Yeah that would be reasonable depending on the use-case. If I need to use the array in a place that expects an array and I can't modify the behavior, however, it won't work. There are also some nice things you can do with static arrays that you can't with just pointers, such as `sizeof(array)` to get the count. – BeeOnRope Nov 07 '16 at 16:00
  • I mean if I want to use this array in some code expects a full array, rather than a pointer and size (or array, offset and size) I won't be able to, unless I could modify that code, which isn't necessarily possible or desirable. – BeeOnRope Nov 07 '16 at 16:12
  • @BeeOnRope: Well, you have not quite specified the motivation behind your question, nor how exactly you are using these arrays in your code, so I "did what I could" under the given amount of information provided... – barak manos Nov 07 '16 at 16:33
  • Yes and it would be reasonable depending on the use case as I pointed out. Unfortunately I needed a statically allocated array as in my snippet. – BeeOnRope Nov 07 '16 at 17:13
1

The problem is that all static storage duration variables work with initializer lists that need to be compile-time constants, as defined by the language. Another variable, even if declared as const, is not considered a compile-time constant. In addition, you can't initialize or directly assign an array from another array.

So you can't do this because of limits in the language. Your version with #defines for the initializer lists is actually a common solution to this. It is a simple solution so it might be the best one.


If you are looking for a way to access the data in different ways, one alternative could be something with unions and C11 anonymous structures:

#include <stdio.h>

#define FOO_INIT 1, 2, 3
#define BAR_INIT 4, 5, 6, 7

#define FOO_SIZE ( sizeof((int[]){ FOO_INIT }) / sizeof(int) )
#define BAR_SIZE ( sizeof((int[]){ BAR_INIT }) / sizeof(int) )

typedef union
{
  int array [FOO_SIZE + BAR_SIZE];
  struct
  {
    int foo [FOO_SIZE];
    int bar [BAR_SIZE];
  };
} foobar_t;



int main (void)
{
  static const foobar_t foobar = { .foo = {FOO_INIT}, .bar = {BAR_INIT} };

  // print 1 2 3 4 5 6 7
  for(size_t i=0; i<FOO_SIZE + BAR_SIZE; i++)
  {
    printf("%d ", foobar.array[i]);
  }
}

Now if you wish to make a hardcopy of the array(s), you can actually just do foobar_t foobar2 = foobar;. C is quite weird, because while it doesn't allow assignment of arrays, it is perfectly fine with assignment of structures/unions containing arrays. (But that will still not work if foobar2 has static storage duration.)

Lundin
  • 195,001
  • 40
  • 254
  • 396
-2
Yup! you can create another array whose size is the sum of both foo and bar . Then you can one by one copy the elements using looping statements. first loop will begin from 0 to the size of first array and second loop will begin from the size of first array to the size of the second array.