91

I want to create a constant static array to be used throughout my Objective-C implementation file, similar to something like this at the top level of my ".m" file:

static const int NUM_TYPES = 4;
static int types[NUM_TYPES] = {
  1,
  2,
  3,
  4 };

I plan on using NUM_TYPES later on in the file, so I wanted to put it in a variable.

However, when I do this, I get the error

"Variably modified 'types' at file scope"

I gather that this may have something to do with the array size being a variable (I don't get this message when I put an integer literal there, like static int types[4]).

I want to fix this, but maybe I am going about it all wrong...I have two goals here:

  1. To have an array which is accessible throughout the file
  2. To encapsulate NUM_TYPES into a variable, so I don't have the same literal scattered about different places in my file

What can I do?

I found this in the C FAQ (11.8): I don't understand why I can't use const values in initializers and array dimensions

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sam
  • 2,620
  • 2
  • 26
  • 28
  • 2
    What happens if you do it as a define instead? `#define kNUM_TYPES 4` ? – Jorge Israel Peña Nov 11 '09 at 02:22
  • That works...for some reason I was trying to stay away from using the preprocessor because I thought I remembered reading that somewhere, but I just did some more research and couldn't find a good reason not to use it in this case. I think it may be less desirable if I'm creating objects in the preprocessor (like `@"An NSString literal"`) The only thing wrong with your piece of code is that there's no need for the semicolon. – Sam Nov 11 '09 at 02:54
  • 1
    The corresponding canonical question for [C](https://en.wikipedia.org/wiki/C_%28programming_language%29): *[Variably modified array at file scope in C](https://stackoverflow.com/questions/13645936/)* – Peter Mortensen Jul 29 '23 at 09:02

6 Answers6

66

The reason for this warning is that 'const' in C doesn't mean constant. It means "read-only". So the value is stored at a memory address and could potentially be changed by machine code.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
larsr
  • 5,447
  • 19
  • 38
  • 4
    Modifying an object defined `const` (such as by casting away `const` from a pointer and storing a value) is undefined behaviour; therefore, the value of such an object is a compile-time or run-time constant (depending on storage duration). The value cannot be used in a constant expression simply because the C standard does not say it can be. (Casting away `const` and storing a value is permitted if the destination object is defined without `const` or dynamically allocated; string literals are not `const` but may not be written to.) – jilles Dec 28 '12 at 21:22
  • 3
    @jilles "could potentially be changed by machine code" does not mean that the author of this answer meant "could potentially changed by C code". Furthermore, this does have another very good reason: there can be `extern` constants in different TUs of which the value is not known when compiling the current TU. –  Nov 29 '13 at 20:11
  • 18
    A way to improve this answer would be to show how to resolve this issue. – George Stocker May 16 '16 at 13:02
  • An attempt to resolve this issue is [CygnusX1's answer](https://stackoverflow.com/questions/1712592/variably-modified-array-at-file-scope/40961072#40961072). – Peter Mortensen Jul 29 '23 at 10:45
  • 1
    But this question is for Objective-C, not C: *"...throughout my Objective-C implementation file"* – Peter Mortensen Jul 29 '23 at 10:56
34

If you're going to use the preprocessor anyway, as per the other answers, then you can make the compiler determine the value of NUM_TYPES automagically:

#define NUM_TYPES (sizeof types / sizeof types[0])
static int types[] = { 
  1,
  2, 
  3, 
  4 };
caf
  • 233,326
  • 40
  • 323
  • 462
  • Wow that's really cool...I did not know that was possible. I assume the cost of this computation is negligible. Might I also assume that a compiler could optimize this to a static value? – Sam Nov 11 '09 at 03:56
  • 2
    Yes, the result of `sizeof` on objects like that is a compile-time constant. – caf Nov 11 '09 at 04:02
22
#define NUM_TYPES 4
Jim Buck
  • 20,482
  • 11
  • 57
  • 74
11

It is also possible to use enumeration.

typedef enum {
    typeNo1 = 1,
    typeNo2,
    typeNo3,
    typeNo4,
    NumOfTypes = typeNo4
}  TypeOfSomething;
7

As it is already explained in other answers, const in C merely means that a variable is read-only. It is still a run-time value. However, you can use an enum as a real constant in C:

enum { NUM_TYPES = 4 };
static int types[NUM_TYPES] = { 
  1, 2, 3, 4
};
CygnusX1
  • 20,968
  • 5
  • 65
  • 109
  • Yes, this is the real answer to the *implied* question for many coming from [C++](https://en.wikipedia.org/wiki/C%2B%2B) (or C++-like, e.g. [Arduino](https://en.wikipedia.org/wiki/Arduino)). – Peter Mortensen Jul 29 '23 at 10:51
  • 1
    Though this question is for Objective-C, not C: *"...throughout my Objective-C implementation file"* – Peter Mortensen Jul 29 '23 at 10:57
3

IMHO, this is a flaw in many C compilers. I know for a fact that the compilers I worked with do not store a "static const" variable at an address, but replace the use in the code by the very constant. This can be verified as you will get the same checksum for the produced code when you use a preprocessors #define directive and when you use a static const variable.

Either way, you should use static const variables instead of #defines whenever possible as the static const is type-safe.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 1
    That sounds pretty bad, since you can take the address of a `static const` variable. The behavior you're describing might be a valid optimization, but it's certainly not something that could always work. – unwind Oct 19 '16 at 07:39
  • 1
    It is actually fine. It is OK for the C compiler to replace individual uses of const global variables with the constant value wherever possible. If all references to a variable are converted to constants, then the compiler can remove it entirely. If you use the address anywhere, it won't be removed. None of that changes that according to the language standard, C does not allow global arrays with an variable as the size, whether the variable is const or not. – Evan May 09 '17 at 17:30
  • How is any of this relevant to a question about Objective-C, which is a different language to C? – Toby Speight Jul 29 '23 at 11:53