我如何在C编译时使用外部数组的大小?

  • 本文关键字:数组 外部 编译 c
  • 更新时间 :
  • 英文 :


我有以下foo.c文件片段:

unsigned char foo[] = {1, 2, 3, 4};
void copy_foo(unsigned char *dest)
{
memcpy(dest, foo, sizeof(foo));
}

我想在另一个C文件(main.c)中声明一个bar数组,其大小与foo相同,以保存copy_foo的输出:

int main(void)
{
unsigned char bar[?];
copy_foo(bar);
return 0;
}

是否有可能在编译时从main.c访问sizeof(foo),以便它可以用来声明bar数组?

通常的"解决方案"解决这个问题的方法是在运行时使用某种堆分配来分配内存,但我正在寻找一个静态的,编译时的解决方案,因为一些嵌入式系统禁止使用动态分配。

当然,我可以将#define FOO_SIZE 4添加到foo.h,但是如果foo更改了FOO_SIZE,则必须手动更改。

这可能吗?

这是我找到的解决方案,供任何感兴趣的人使用。如果有人有更好的解决方案,我仍然有兴趣看看:)

main.c:

#include "foo.h"
int main(void)
{
unsigned char bar[FOO_SIZE];
copy_foo(bar);
return FOO_SIZE;
}

foo.h:

#define FOO (unsigned char []){1, 2, 3, 4}
#define FOO_SIZE sizeof(FOO)
void copy_foo(unsigned char *dest);

foo.c:

#include <string.h>
#include "foo.h"
unsigned char foo[] = FOO;
void copy_foo(unsigned char *dest)
{
memcpy(dest, foo, FOO_SIZE);
}

这要归功于"复合字面量",它允许我们定义"匿名";数组。FOO被定义为这些复合字面值之一,它可以用来初始化foo数组。有趣的是,它也可以在sizeof中使用,下面这行:

unsigned char bar[FOO_SIZE];

评估:

unsigned char bar[sizeof((unsigned char []){1, 2, 3, 4})];

是完全合法的

现在要证明在编译时的结果确实是4,并且它不会将数组的额外副本添加到数据部分:

我用:

编译
gcc main.c foo.c -S

Infoo.s:

.globl  foo
.data
.type   foo, @object
.size   foo, 4
foo:
.ascii  "01020304"

main.s中没有这样的数组,所以编译器没有复制数组。这是有意义的,因为数组是在sizeof中声明的,所以编译器应该足够聪明,可以优化它并将其替换为4。

为了检查该值在编译时是否可用,我将其作为main的返回值返回,它编译为:

movl    $4, %eax
movq    -8(%rbp), %rdx
xorq    %fs:40, %rdx
je  .L3
call    __stack_chk_fail@PLT
.L3:
leave
.cfi_def_cfa 7, 8
ret

返回值通过eax寄存器传递,我们可以看到它得到的直接值是4,这证明编译器知道这个值是什么。

添加到头文件:

// foo.h
extern const size_t foo_len;

并在数组定义的正下方添加:

// foo.c
const size_t foo_len = sizeof foo / sizeof foo[0];

然后你可以在你的主函数或任何你想要的地方使用foo_len,而不必担心手动维护该值。

然而,这是在C中使此工作的唯一方法,因为静态数组的长度在编译后丢失,这在链接之前发生。这意味着您必须为每个希望使用其预定义长度的数组添加类似的行。

您可以只使用带有访问器的静态数组。

// foo.h
static unsigned char _foo[] = {1,2,3,4};
#define FOO_SIZE  (sizeof(_foo))
unsigned char *foo(void); // if you ever need to use _foo
void copy_foo(unsigned char*);
// foo.c
unsigned char *foo(void) {
return _foo;
}
void copy_foo(unsigned char *dest) {
memcpy(dest, foo(), FOO_SIZE);
}
// main.c
#include "foo.h"    
int main(void) {
unsigned char bar[FOO_SIZE];
copy_foo(bar);
return FOO_SIZE;
}

只要确保你从来没有在任何地方使用_foo在你的代码除了foo.c,因为它是static和存在于每个源文件,包括foo.h。编译器将从所有源文件中删除未使用的静态全局变量,只在foo.c文件中留下一个_foo

如果数组在不同的模块中定义,则必须在一个通用的头文件中声明它,并指定每个模块使用或检查一致性的长度:

foo。

extern unsigned char foo[4];

foo.c

#include "foo.h"
// if the size in foo.h is not correct, this will cause an error
unsigned char foo[] = { 1, 2, 3, 4 };
void copy_foo(unsigned char *dest) {
memcpy(dest, foo, sizeof(foo));
}

c

#include "foo.h"
int main() {
unsigned char bar[sizeof(foo) / sizeof(*foo))];
copy_foo(bar);
return 0;
}

如果数组的长度不能在头文件中指定,你可以定义一个外部变量foo_len,但它不会是一个常量表达式,所以bar将是一个VLA(可变长度数组,C99在C17中成为可选的功能)。

foo。

extern unsigned char foo[]; // optional
extern int foo_len;

foo.c

#include "foo.h"
// if the size in foo.h is not correct, this will cause an error
unsigned char foo[] = { 1, 2, 3, 4 };
int foo_len = sizeof(foo) / sizeof(*foo);
void copy_foo(unsigned char *dest) {
memcpy(dest, foo, sizeof(foo));
}

c

#include "foo.h"
int main() {
unsigned char bar[foo_len];
copy_foo(bar);
return 0;
}

最新更新