在两个模块之间共享 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
。因为 arr
是 int
而不是 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;
}
在实际应用中,这个“数组”当然是有意义的东西。
我想在两个 .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
。因为arr
是int
而不是 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;
}
在实际应用中,这个“数组”当然是有意义的东西。