1

There are a number of global variables, let's say for example someglobalvalue1, someglobalvalue2, etc, that are updated dynamically from a different part of the app I'm working on. In the part that I am currently working on, I need to gather these variables into an integer array. Because there are a lot of these variables in different groups, I have separate functions like GetGroupOne(), GetGroupTwo(), etc to insert the globals into an array and return it to the main function. I'm using this page as a guide for how to return int arrays from functions: http://www.tutorialspoint.com/cprogramming/c_return_arrays_from_function.htm

So for example:

int main() {
   int *array;
   array = GetGroupOne();
   /* do stuff with array */
   return 0;
}

int * GetGroupOne() {
  static int array[GROUP_ONE_LENGTH] = { someglobalvalue1, someglobalvalue2, someglobalvalue3 };

  return array;
}

So, trying to compile this (by the way, I am limited to a C90 compiler) gets me this error:

Error “initializer element is not constant” when trying to initialize variable with const

So, I did find Error "initializer element is not constant" when trying to initialize variable with const and other threads that seemed to suggest that what I was trying to do, initialize using globals, is not possible. So I bit the bullet and did this instead:

int * GetGroupOne() {
  static int array[GROUP_ONE_LENGTH];
  array[0] = someglobalvalue1;
  array[1] = someglobalvalue2;
  array[2] = someglobalvalue3;
  /* etc... */

  return array;
}

Now, this works but it is incredibly ugly and it hurts my soul to look at it. Especially because there are multiple groups, some of which have over a hundred entries, so I have to insert each global into the array individually. I know how I would handle this in higher level languages, but C is still somewhat of a mystery to me. But I'm thinking there MUST be some better way to handle this problem. Can anyone nudge me in the right direction?

Thank you.

Community
  • 1
  • 1
mrrrow
  • 98
  • 3
  • 9
  • 3
    Why is your program so badly designed that it has hundreds of global variables? – ooga Apr 30 '14 at 01:06
  • Static variables are **not** initialized every time. And your static array contains value. So it is a better idea to stick with your solution 2 or read David's solution. But a good design doesn't(or atleast hardsly) need global variable. – Mohit Jain Apr 30 '14 at 01:36
  • 1
    Why not just have an array to begin with? – Crowman Apr 30 '14 at 01:36
  • To expand on Paul's suggestion you can just define the global var to be a member of the array :: `int array[3]; ... #define someglobalvar1 array[0] ...` – technosaurus Apr 30 '14 at 03:03
  • Ooga - I cannot answer that, I did not design it. Paul - I'm not clear on how I can make a global array when I do not declare the globals in my code and have no control over that. – mrrrow May 01 '14 at 02:32

2 Answers2

2

Putting aside my own soul's pain over a design that calls for hundreds of global variables, there is at least one way to improve the look of your implementation, using a function with a variable-length argument list. It would look something like this (untested):

  #include <stdarg.h>
  int * CompileIntegerArray( int *dest, unsigned int count, ... ) {
    int *put = dest;
    va_list arglist;
    va_start( arglist, count );
    for( ; count != 0; --count ) {
      *put++ = va_arg( arglist, int );
    }
    va_end( arglist );
    return dest;
  }

...

  int * GetGroupOne() {
    static int array[GROUP_ONE_LENGTH];
    return CompileIntegerArray( array, GROUP_ONE_LENGTH,
      someglobalvalue1, 
      someglobalvalue2, 
      someglobalvalue3 
      );
  }

Realize that there are terrible things about what you are doing, though. At a minimum, whenever a group changes length you have to update the length and ensure that the right number of variables are in the call to CompileIntegerArray, or risk introducing delayed stack bombs into your code.

There are of course some (minor) performance implications to this approach, and some compilers might choke on hundreds of arguments to a function call.

[Edited to add an alternative]

There's another thing you can do that will improve the look a little bit and still make it easier to change the order of the array elements. It is somewhat faster, and does not rely on a separate function to process the array. This will also give you the opportunity to do some error checking. Use the preprocessor:

#define _el(x) array[ index++ ] = x;

int * GetGroupOne() {
  static int array[GROUP_ONE_LENGTH];
  int index = 0;

  _el( someglobalvalue1 )
  _el( someglobalvalue2 )
  _el( someglobalvalue3 )

  if( index != GROUP_ONE_LENGTH ) {
    puts( "GAAK!" );
    exit(1);
  }
  return array;
}

David O'Riva
  • 696
  • 3
  • 5
  • Oh, I know. I don't have control over the part with hundreds of globals though unfortunately. I will try this tomorrow, thank you! – mrrrow Apr 30 '14 at 02:13
  • I'd also add in a static flag so that you only go through this drama once per run – M.M Apr 30 '14 at 02:37
  • @MattMcNabb I'm guessing that the implementation calls GetGroupOne on a regular basis to get an updated "view" of the global state. If wholesale changing of the implementation were an option, I sort of like Daniel's solution of returning an array of pointers to globals. Or better something like "struct GroupOne { int *value1; int *value2; }" so that you could reach through to what you're looking for by name instead of index. Then it would be possible to hide all the global variables and just expose the groups. – David O'Riva Apr 30 '14 at 02:46
  • I used your alternative solution. It works great and looks much nicer. Thank you. One question: what is "el" an abbreviation/acronym for here? Just curious why you chose that name. – mrrrow Apr 30 '14 at 22:10
  • @mrrrow "el" alludes to the array element you are setting with each invocation of the macro. – David O'Riva May 01 '14 at 01:22
0

One option is to make your static array contain pointers to the global variables (which does count as a compile-time constant):

int **get_group_one(void)   // (void) instead of () is important
{
// note the `const`, your system will love you for it
// could also add a compile-time test that you actually do have GROUP_ONE_LENGTH entries
static int *const array[GROUP_ONE_LENGTH] = { &global0, &global1, &global2 };

    return array;
}

and then access your globals via a macro

#define G1(n) ( *(get_group_one()[n])  )

You could add some error checking to avoid undefined behaviour on range errors, if you try G1(n) where n is out of range then you either access a dummy variable, or call an error handler, etc.

If your compiler supports inline functions then that would be tidier than a macro.

BTW is there any reason you can't just have your global variables be in an array in the first place?

M.M
  • 138,810
  • 21
  • 208
  • 365
  • I'm sorry, I don't understand your question about why I can't have my global variables in an array in the first place. That is what I am trying to do, to put them in an array. What exactly do you mean, put them in an array in the first place? The place where the global variables are declared is not my code, I have no control over that. – mrrrow Apr 30 '14 at 18:40
  • I went with David's solution because it was a little easier to read, no pointers to pointers (which are a concept I haven't quite wrapped my mind around yet), and no need for the & operator (easier to read), but thank you regardless for providing an alternative. I will come back to it later when I understand pointers better to study it as an example. – mrrrow Apr 30 '14 at 22:12
  • By "put them in an array in the first place", I mean having at file scope `int globals_array[GROUP_ONE_LENGTH];`, instead of separate variables `someglobalvalue1`, `someglobalvalue2` etc. – M.M May 01 '14 at 04:13
  • But then I would still need to put the separate variables into the array at some point... I need the data that is stored in the globals from a different part of the program that I don't control. – mrrrow May 01 '14 at 06:13
  • An array *is* variables. My last paragraph suggested to use this array of variables instead of having separate variables. (although you have just explained that your program structure doesn't allow this). – M.M May 01 '14 at 06:18
  • OK, gotcha. I would definitely do it that way if I could! :) Thank you for your advice & patience regardless. – mrrrow May 01 '14 at 16:07