Share sizeof(array) between two modules

Viewed 215

I want to share the value of sizeof(array) between two .c modules. The array is initialized in file A, so compiler knows the size at compile time and then I want to use the size of this array in another B file.

Example:
In A.c file:

int arr[] = {1, 2, 3};
.
.
.

for (int i = 0; i < sizeof(arr); i++); // that works

In A.h file:

extern int arr[];

In B.c file:

#include "A.h"
.
.
.

for (int i = 0; i < sizeof(arr); i++); // here compiler doesn't know size of this arr

Is there a way to make this work? I know why this doesn't work but maybe there is a sneaky trick to workaround this.

5 Answers

Is there a way to make this work? I know why this doesn't work but maybe there is a sneaky trick to workaround this.

This unfortunately is not very sneaky, but it works...

In some.h

//declare as extern for project global scope
extern int arr[];
extern size_t gSizeArray;

Then in some.c

//define and initialize extern variables in 1 (and only one) .c file, 
int arr[] = { 1,2,3 };
size_t gSizeArray = sizeof(arr)/sizeof(arr[0]);

In someother.c that includes some.h

#include "some.h"
//you can now see the value of gSizeArray in this file

printf("%zu\n%d,%d,%d\n", gSizeArray, arr[0], arr[1], arr[2]);//should output

3
1,2,3

caveat
The value of using an extern in this fashion is that the value of that variable can change in one module, and that same value can be seen in any .c file that includes some.h

corollary
The problem of using an extern in this fashion is that the value of that variable can change in one module, and that same value can be seen in any .c file that includes some.h.

(or in other words, be careful. Globals, in particular extern globals can be helpful, but they can also be dangerous. Particularly for maintainers of the code who are not also the author of the code, who may not be aware a variable is of extern scope, and use it unwittingly in inappropriate ways.

By the way, there is probably more than you'll ever want to know about using extern scope in this post.

Make the initializer a macro, share it through the header, and then you can use the compiler to recount the elements for the extern declaration:

//header.h
#define ARR_INIT { 1, 2, 3}
extern int arr[sizeof((int[])ARR_INIT)/sizeof(int)];

//file.c
#include "header.h"
int arr[] = ARR_INIT;

( On gcc/clang/tinycc, you can also do:

//header.h
#define ARR_INIT (int[]){ 1, 2, 3}
extern int arr[sizeof(ARR_INIT)/sizeof(ARR_INIT[0])];

//file.c
#include "header.h"
int arr[] = ARR_INIT;

which is more typesafe but won't work with -Wpedantic as standard C doesn't allow compound literals as initializers for objects with static storage duration. )

You could also share the size through a regular global, but that has disadvantages as such a size would no longer be an integer constant expression and so it wouldn't be usable where those are required (array sizes, bitfield-widths, case labels, static initializers).

You can do it f.e. by declaring both, the size object (which contains the amount of elements of arr) and the array arr, with the extern storage-class specifier so that they have external linkage in the header A.h. This header you include in both .c source files.

The value of the size object as well as the size of the array arr (by its initializer list) is specified in A.c but can be used in B.c.


The A.h file:

 #include <stdio.h>     // for size_t.

 extern int arr[];
 extern size_t size;

The A.c file:

 #include "A.h"
 #include <stdio.h>     // for size_t. 

 int arr[] = { 1, 2, 3 };
 size_t size = sizeof (arr) / sizeof (arr[0]);

 for ( size_t i = 0; i < size; i++ ) 
 {
      arr[i] = arr[i] - 1;
 }

The B.c file:

 #include "A.h"
 #include <stdio.h>

 for ( size_t i = 0; i < size; i++ ) 
 {
      printf("%d\n", arr[i]);
 }

Output:

 0
 1 
 2

Side Note:

  • sizeof(arr) does not yield the amount of elements in arr; It yields the amount of bytes allocated for the whole array arr. Since arr is an array of type int and not f.e. char, where the amounts of elements is equal to the amount of allocated byte, this result is different to the amount of elements.

    To gain the amount of elements use sizeof(arr) / sizeof(arr[0]).

no magic needed or another global variables.

in the .h file

extern int arr[3];

and then

int main(void)
{
    printf("%zu\n", sizeof(arr)/ sizeof(arr[0]));
}

When you say extern int arr[]; in the header file, you tell the compiler that:

  • This variable is defined later. It isn't allocated here, but elsewhere.
  • The size of this array is unknown (incomplete). I'll complete it later when I define the variable.

C works with something called translation units. A translation unit is a .c file and all the headers it includes. In your example A.c + A.h forms one translation unit, and B.c + A.h forms another. These are compiled independently.

In translation unit A.c + A.h, the compiler defines the variable and also gives it a size (3 int). Therefore the size of this array is known throughout that translation unit.

In translation unit B.c + A.h, you never define the variable. The compiler just knows that the array is defined "elsewhere", but it has no way to know the size.


The cause is extern spaghetti programming, where the same global variable is shared all over the place. The proper solution is to never do that. Use sound program design with private encapsulation instead. Let the array be allocated where it makes sense for it to be. Something along the lines of this (silly example):

array.h

int    get_array_item (size_t i);
void   set_array_item (size_t i, int val);
size_t get_array_size (void);

array.c

#include "array.h"

static int arr[] = {1,2,3};

int get_array_item (size_t i)
{
  assert(i < sizeof arr); // optional error handling
  return array[i];
}

void set_array_item (size_t i, int val)
{
  assert(i < sizeof arr); // optional error handling
  array[i] = val;
}

size_t get_array_size (void)
{
  return sizeof arr;
}

In a real application, this "array" would of course be something meaningful instead.

Related