4

I have an array with size determined during compile-time defined in a source file.

const int array[] = {1, 3, 3, 7};

The number of the elements could change in the future so I'd rather not hardcode it in the brackets.

This array needs to be accessed from several source files so I'm trying to add an extern declaration to it in a header. However I'm not sure this is possible due to the implicit array size. I tried two variants:

extern const int array[]; // Warning: size of symbol `array' changed from 8 to 16
extern const int *array; // error: conflicting types for 'array'

Is it possible to do this or should I look for a work-around?

martinkunev
  • 1,364
  • 18
  • 39

1 Answers1

6

In the header that declares the variable, write:

extern const int array[];

You're right, though, that the other files won't know the size of the array. That's trickier. You might perhaps use in the header:

extern const int array[];
extern const size_t array_size;

and where the array is defined:

const int array[] = {1, 3, 3, 7};
const size_t array_size = sizeof(array) / sizeof(array[0]);

You'd include the header where the array is defined to ensure that the cross-references are correct, in agreement. You'll get no complaint from me if you choose to use int instead of size_t (but if you set your compiler to fussy enough, it may have different views on the subject).

Note that the array size is not a 'integer constant' within the meaning of the standard; it could not be used in a case label, for example. If you declare another array using array_size, it will be a VLA — variable length array. Such arrays cannot be declared at file scope (or with static storage class inside a function).

See also How do I use extern to share variables between source files?

Illustration

An MCVE (Minimal, Complete, Verifiable Example) for the answer:

ext-def.h

#include <stddef.h>

extern const int array[];
extern const size_t array_size;

ext-def.c

#include "ext-def.h"

const int array[] = {1, 3, 3, 7};
const size_t array_size = sizeof(array) / sizeof(array[0]);

ext-use.c

#include "ext-def.h"
#include <stdio.h>

int main(void)
{
    for (size_t i = 0; i < array_size; i++)
        printf("%zu: %d\n", i, array[i]);
    return 0;
}

Compilation

Using GCC 7.2.0 on a MacBook Pro running macOS High Sierra 10.13.2, using the options specified by martinkunev in a comment:

$ gcc -std=c99 -pthread -O2 -fstrict-aliasing -fomit-frame-pointer -pedantic -o ext-def ext-def.c ext-use.c
$ ./ext-def
0: 1
1: 3
2: 3
3: 7
$

Using my default compilation options (C11 not C99):

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -c ext-def.c
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -c ext-use.c
$

Equally warning-free under either set of options. You can add -pedantic to the C11 command lines without getting any warning, too.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Can I assume the warning about size is harmless if I never use `sizeof(array)`? – martinkunev Dec 27 '17 at 23:56
  • 1
    Which warning are you getting exactly? (And which compiler options?) . You should never assume warnings are harmless; you should aim to avoid having any compiler warnings showing, precisely so that if one turns up, you treat it as harmful. C++ compilers have different views about what happens with `const int variable = value;` type notations — they're not globally visible, even without a `static`. – Jonathan Leffler Dec 27 '17 at 23:57
  • I compile with `gcc -std=c99 -pthread -O2 -fstrict-aliasing -fomit-frame-pointer -pedantic` and I get `size of symbol array changed from 8 in foo.o to 16 in bar.o` (bar is where the definition of array is) – martinkunev Dec 27 '17 at 23:59
  • 1
    I fear you must have done something wrong, then. I now show in my answer what is necessary — in what's approximately an MCVE. Without seeing exactly what you did, it is hard to know where you're going wrong. Note that the header has no initializers in it. – Jonathan Leffler Dec 28 '17 at 00:11
  • I figured out what was causing the warning. In the real code the array is of a struct type and in the hurry I was defining the array inside the struct definition (`extern const struct {...} array[];`). When I split them, the warning disappeared. – martinkunev Dec 28 '17 at 00:13
  • 1
    That would cause confusion. You'd need a structure tag, I believe, to achieve the separation properly. Or a `typedef` for the anonymous structure type. Glad you managed to resolve it, though; I'd have had a hard time guessing that was the problem from the immediate problem report. – Jonathan Leffler Dec 28 '17 at 00:15
  • I was trying to give a minimal example here and apparently it was *too* minimal. If I had pasted the whole code, it would have taken more time for others to read it and I probably would have gotten a ton of comments "you're doing this and that wrong". – martinkunev Dec 28 '17 at 00:19
  • 1
    It can be a delicate balancing act. Consider using just two fields in the structure for an MCVE (unless you're doing a doubly-linked list; then you need 3 — two pointers and an `int` or `char [n]` value). Something along those lines is sufficient. Creating an MCVE often involves writing new code parallel to (equivalent to) the real code, masking all the nitty-gritty details of the 'real code' while still getting the compilation warnings/errors. Ruthless hacking and a good version control system (and a branch) often help. – Jonathan Leffler Dec 28 '17 at 00:21