我正在一个项目中,我需要在C中创建许多全局变量的地址范围(不可能使用C ++),在Clang中使用 。对于完整类型的符号,这很容易以符合标准的方式进行:
typedef struct range {
void* begin;
void* end;
} range;
extern int foo;
range foo_range = { &(&foo)[0], &(&foo)[1] };
但是正如我所说,它之所以有效是因为C编译器静态知道foo
的大小,因此它能够将&(&foo)[1]
解析为foo + 4个字节(假设sizeof(int)
为4 , 当然)。这不适用于不完整类型的符号:
struct incomplete;
struct trailing_array {
int count;
int elements[];
};
extern int foo[];
extern struct incomplete bar;
extern struct trailing_array baz;
range foo_range = { &(&foo)[0], &(&foo)[1] };
// error: foo has incomplete type
range bar_range = { &(&bar)[0], &(&bar)[1] };
// error: bar has incomplete type
range bar_range = { &(&baz)[0], &(&baz)[1] };
// this one compiles, but the range excludes the elements array
但是,对我来说,更多地描述这些符号不是问题。例如,我可以轻松添加元数据:
// foo.h
extern int foo[];
extern size_t foo_size;
// foo.c
int foo[] = {1,2,3};
size_t foo_size = sizeof(foo);
除了对foo.c
以外的引用无济于事,因为foo_size
不是编译时常量,因此这行不通:
range foo_range = { &foo, (void*)&foo + foo_size };
// error: foo_size not a compile-time constant
将要工作的工作是获得符号的地址,该符号的地址恰好在我的对象结束处。例如,如果我用以下汇编代码定义foo
:
_foo:
.long 1
.long 2
.long 3
_foo_end:
然后,在我的C代码中,我可以拥有:
extern int foo[];
extern int foo_end;
range foo_range = { &foo, &foo_end };
这有效地解决了我的问题。
但是,尽管我可以灵活地添加符号,但不能灵活地将每个全局声明重写为文件级汇编语句。所以,我的问题是:使用clang,与我最接近的是什么?
我专门使用Apple的链接器,但是如果您有适用于GNU ld / gold或lld的解决方案,我仍然会采用它,看看是否也可以在这里使用它。
答案 0 :(得分:0)
嗯,如果要在另一个翻译单元中定义它,则没有真正的方法。如果需要,可以包含一个types.h
文件,其中包含内容
struct incomplete {
char data[SIZE];
}
SIZE是您想要的任何整数,并且对每个全局变量都执行相同的操作。但是,这将与将来的定义相交。真的,您必须选择
#define INCOMPLETE_SIZE 5
然后将其用于range = { &bar, (void*)&bar + INCOMPLETE_SIZE }
。
“不完整类型”只是用于正确描述如何解析的一些标准术语
struct A {
A* ptr;
}
据我所知,它们并没有真正被其他人使用。
我也不推荐使用&(&foo)[0], &(&foo)[1]
作为获取一系列指针的方法,因为它非常深奥/难以阅读。首选&foo, &foo + 1
。您可以通过执行bar
来了解如何将其变成&bar, (void*)&bar + SIZE
的解决方案,其中SIZE是您必须在代码中的某处指定的常量(可以通过声明并使用sizeof
/ &foo+1
解决方案,或使用SIZE
定义#define
)