简体   繁体   English

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

[英]How can I use the size of an external array at compile time in C?

I have the following snippet from some foo.c file:我有一些foo.c文件的以下片段:

unsigned char foo[] = {1, 2, 3, 4};

void copy_foo(unsigned char *dest)
{
    memcpy(dest, foo, sizeof(foo));
}

And I want to declare a bar array in another C file ( main.c ) that has the same size as foo , to hold the output of copy_foo :我想在另一个 C 文件( main.c )中声明一个与foo大小相同的bar数组,以保存 copy_foo 的copy_foo

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

Is it possible to get access to sizeof(foo) from main.c at compile time, so that it could be used to declare the bar array?是否可以在编译时从main.c访问sizeof(foo) ,以便可以用来声明bar数组?

The usual "solution" to this problem is to use some sort of heap allocation to allocate memory at runtime, but I am looking for a static, compile time solution, as some embedded systems prohibit the use of dynamic allocation.这个问题的通常“解决方案”是在运行时使用某种堆分配来分配 memory,但我正在寻找 static,编译时解决方案,因为某些嵌入式系统禁止使用动态分配。

Of course, I can add #define FOO_SIZE 4 to foo.h , but then if foo changes FOO_SIZE will have to be manually changed as well.当然,我可以将#define FOO_SIZE 4添加到foo.h ,但是如果foo更改FOO_SIZE也必须手动更改。

Is this possible?这可能吗?

Add this to the header file:将此添加到 header 文件中:

// foo.h
extern const size_t foo_len;

And add this right below the definition of the array:并将其添加到数组定义的正下方:

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

And then you can use foo_len in your main function or wherever you want, without having to worry about manually maintaining that value.然后你可以在你的主 function 或任何你想要的地方使用foo_len ,而不必担心手动维护该值。

This, however, is the only way to make this work in C, as the lengths of static arrays are lost after compiling, which happens before linking.然而,这是在 C 中进行这项工作的唯一方法,因为 static arrays 的长度在编译后丢失,这发生链接之前。 This means you have to add similar lines for every array you want to use their predefined lengths.这意味着您必须为要使用其预定义长度的每个数组添加类似的行。

Here's the solution I found, for anyone interested.这是我找到的解决方案,适合任何感兴趣的人。 If anyone has a better solution I would still be interested to see it:)如果有人有更好的解决方案,我仍然有兴趣看到它:)

main.c : main.c

#include "foo.h"

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

foo.h : foo.h

#define FOO (unsigned char []){1, 2, 3, 4}
#define FOO_SIZE sizeof(FOO)

void copy_foo(unsigned char *dest);

foo.c : foo.c

#include <string.h>
#include "foo.h"

unsigned char foo[] = FOO;

void copy_foo(unsigned char *dest)
{
    memcpy(dest, foo, FOO_SIZE);
}

This works thanks to "Compound literals" , which allow us to define "anonymous" arrays.这要归功于“复合文字” ,它允许我们定义“匿名”arrays。 FOO is defined as one of those compound literals, and it can be used to initialize the foo array. FOO被定义为这些复合文字之一,它可用于初始化foo数组。 The interesting part is that it could also be used inside a sizeof , so the line:有趣的部分是它也可以在sizeof中使用,所以行:

unsigned char bar[FOO_SIZE];

evaluates to:评估为:

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

which is perfectly legal.这是完全合法的。

Now to prove that this really evaluates to 4 at compile time , and that it doesn't add extra copies of the array to the data section:现在证明这确实在编译时计算为 4 ,并且它不会将数组的额外副本添加到数据部分:

I compiled with:我编译:

gcc main.c foo.c -S

In foo.s :foo.s

    .globl  foo
    .data
    .type   foo, @object
    .size   foo, 4
foo:
    .ascii  "\001\002\003\004"

There is no such array in main.s so the compiler didn't copy the array. main.s中没有这样的数组,所以编译器没有复制数组。 This makes sense, because the array is declared inside a sizeof, so the compiler should be smart enough to optimize it and replace it with a 4.这是有道理的,因为数组是在 sizeof 中声明的,所以编译器应该足够聪明,可以优化它并用 4 替换它。

To check that the value is available at compile time, I returned it as the return value of main , which compiled to:为了检查该值在编译时是否可用,我将它作为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

The return value is passed through the eax register, and we can see that it got the immediate value of 4, which proves that the compiler knew what that value is.返回值通过 eax 寄存器传递,我们可以看到它得到了立即数 4,这证明编译器知道那个值是什么。

You can use just a static array with accessor.您可以只使用带有访问器的 static 阵列。

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

Just make sure you never ever use _foo anywhere in your code except in foo.c , because it's static and exists in each source file that includes foo.h .只要确保你永远不会在代码中的任何地方使用_foo ,除了foo.c ,因为它是static并且存在于每个包含foo.h的源文件中。 Compiler will remove unused static global variables from all source files leaving only a single _foo in foo.c file.编译器将从所有源文件中删除未使用的 static 全局变量,在foo.c文件中只留下一个_foo

If the array is defined in a different module, it must be declared in a common header file with a specified length for every module to use or check consistency:如果数组在不同的模块中定义,则必须在具有指定长度的通用 header 文件中声明,以便每个模块使用或检查一致性:

foo.h foo.h

extern unsigned char foo[4];

foo.c 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));
}

main.c main.c

#include "foo.h"

int main() {
    unsigned char bar[sizeof(foo) / sizeof(*foo))];
    copy_foo(bar);
    return 0;
}

If the length of the array cannot be specified in the header file, you could define an external variable foo_len , but it will not be a constant expression, so bar will be a VLA (variable length array, a C99 feature made optional in C17).如果数组的长度不能在 header 文件中指定,你可以定义一个外部变量foo_len ,但它不会是一个常量表达式,所以bar将是一个 VLA(可变长度数组,C99 特性在 C17 中成为可选) .

foo.h foo.h

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

foo.c 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));
}

main.c main.c

#include "foo.h"

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 编译时确定数组大小的C结构 - C struct with array size determined at compile time 如何在C中更改数组的大小? - How can I change the size of an array in C? 如何在 C 的编译时打印 sizeof() 的结果? - How can I print the result of sizeof() at compile time in C? 我可以在编译时使用gcc扩展强制显式初始化数组大小吗? - Can I enforce explicitly-initialized array size at compile-time with gcc extensions? 初始化C结构数组,其大小在编译时未知 - Initializing a C struct array, with a size not known at compile time 如何编译使用不推荐使用的功能(例如gets())的C程序 - How can I compile C programs that use deprecated functions, such as gets() 可以在c中的运行时确定数组的大小吗? - can size of array be determined at run time in c? 如何在C / Objective-C中获取数组的大小? - How can I get the size of an array in C / Objective-C? 如何在外部服务器上安装和使用嵌入式 C 的编译器? - How can I install and use compilers for embedded C on an external server? 对于 C 项目,如何有效地修补外部库并将其编译到 Makefile 中? - How can I efficiently patch an external library and compile it in a Makefile for a C project?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM