在两个模块之间共享 sizeof(array)

Share sizeof(array) between two modules

我想在两个 .c 模块之间共享 sizeof(array) 的值。该数组在文件 A 中初始化,因此编译器在编译时知道大小,然后我想在另一个 B 文件中使用此数组的大小。

示例:
在 A.c 文件中:

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

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

在 A.h 文件中:

extern int arr[];

在 B.c 文件中:

#include "A.h"
.
.
.

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

有没有办法让它工作?我知道为什么这不起作用,但也许有一个偷偷摸摸的技巧可以解决这个问题。

有没有办法让它工作?我知道为什么这不起作用,但也许有一个偷偷摸摸的技巧来解决这个问题。

不幸的是,这不是很偷偷摸摸,但它确实有效...

some.h

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

然后在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]);

someother.c 中,其中包括 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

警告
以这种方式使用 extern 的 value 是该变量的值可以在一个模块中更改,并且可以在任何 .c 文件中看到相同的值 包括some.h

推论
以这种方式使用 extern 的 问题 是该变量的值可以在一个模块中更改,并且可以在任何 .c 文件中看到相同的值 包括 some.h.

(或者换句话说,要小心。全局变量,尤其是 extern 全局变量可能会有帮助,但它们也可能很危险。特别是对于不是代码作者的代码维护者,他们可能不知道变量属于 extern 范围,并以不适当的方式无意中使用它。

顺便说一句,关于使用 extern 作用域 in this post.

,您可能想知道的更多

把初始化器做成一个宏,通过header共享,然后就可以用编译器重新计算extern声明的元素了:

//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;

( 在 gcc/clang/tinycc 上,您还可以:

//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;

类型更安全,但不能与 -Wpedantic 一起使用,因为标准 C 不允许复合文字作为具有静态存储持续时间的对象的初始值设定项。 )

您也可以通过常规全局共享大小,但这有缺点,因为这样的大小将不再是整数常量表达式,因此它不能用于需要的地方(数组大小、位域-宽度、案例标签、静态初始值设定项)。

你能做到f.e。通过声明 size object(其中包含 arr 的元素数量)和数组 arr,以及 extern storage-class说明符,以便它们在 header A.h 中具有外部链接。此 header 您包含在两个 .c 源文件中。

size object 的值以及数组 arr 的大小(由其初始化列表)在 A.c 中指定,但可以使用在 B.c.


A.h 文件:

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

 extern int arr[];
 extern size_t size;

A.c 文件:

 #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;
 }

B.c 文件:

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

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

输出:

 0
 1 
 2

旁注:

  • sizeof(arr) 不产生 arr 中的元素数量;它产生为整个数组分配的字节数 arr。因为 arrint 而不是 f.e 类型的数组。 char,其中元素数量等于分配的字节数量,此结果与元素数量不同。

    要获取元素数量,请使用 sizeof(arr) / sizeof(arr[0])

不需要魔法或其他全局变量。

在.h文件中

extern int arr[3];

然后

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

当您在 header 文件中说 extern int arr[]; 时,您告诉编译器:

  • 这个变量稍后定义。它不是在这里分配的,而是在别处分配的。
  • 此数组的大小未知(不完整)。等我定义变量的时候再补全。

C 使用称为 翻译单元 的东西。翻译单元是一个 .c 文件及其包含的所有 header。在您的示例中,A.c + A.h 形成一个翻译单元,B.c + A.h 形成另一个翻译单元。这些都是独立编译的。

在翻译单元A.c + A.h中,编译器定义了变量并赋予它一个大小(3 int)。因此,这个数组的大小在整个翻译单元中都是已知的。

在翻译单元 B.c + A.h 中,您永远不会定义变量。编译器只知道数组是在“别处”定义的,但无法知道大小。


原因是 extern 意大利面条式编程,其中到处共享相同的全局变量。正确的解决办法是永远不要那样做。改用私有封装的合理程序设计。让数组分配到对它有意义的地方。类似于这个(愚蠢的例子):

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;
}

在实际应用中,这个“数组”当然是有意义的东西。