![](/img/trans.png)
[英]How to output the offset of a member in a struct at compile time (C/C++)
[英]How can I print the offset of a struct member at compile time?
給定一個結構,例如:
struct A {
char a;
char b;
} __attribute__((packed));
我希望在編譯時打印結構中b
的偏移量(在本例中為 1) - 我不想運行程序並調用類似printf("%zu", offsetof(struct A, b));
因為在我的平台上打印並非易事。 我希望編譯器本身打印偏移量,例如:
> gcc main.c
The offset of b is 1
我嘗試了一些使用#pragma message
和offsetof
的方法,我最接近的是:
#define OFFSET offsetof(struct A, b)
#define XSTR(x) STR(x)
#define STR(x) #x
#pragma message "Offset: " XSTR(OFFSET)
只打印:
> gcc main.c
main.c:12:9: note: #pragma message: Offset: __builtin_offsetof (struct A, b)
它不打印數字偏移量。 可以使用_Static_assert
在編譯時對偏移量進行二進制搜索 - 但我的真實結構很大,這可能會有點麻煩。
我懷疑聲明的約束“我希望編譯器本身打印偏移量”是一個 XY 問題,我們只需要由用於構建的系統上的構建工具打印偏移量,而不是專門由編譯器打印。
在這種情況下,GCC 和 Clang 能夠在其匯編輸出中包含任意文本,<\/a>並在該文本中包含各種數據操作數,包括結構偏移的立即值。
在任何函數中,包括以下幾行:
#if GenerateStructureOffsets
__asm__("# offsetof(struct A, b) = %c0" : : "i" (offsetof(struct A, b)));
#endif
給定這個宏:
#define PRINT_OFFSETOF(A, B) char (*__daniel_kleinstein_is_cool)[sizeof(char[offsetof(A, B)])] = 1
令人驚訝的是,看起來__builtin_choose_expr
在__deprecated__
函數屬性中工作。 以下程序:
#include <stddef.h>
struct A {
char a;
char b;
} __attribute__((packed));
#define printval_case(x, xstr, y, ...) __builtin_choose_expr(x == y, xstr"="#y, __VA_ARGS__)
#define printval(x) do { \
__attribute__((__deprecated__( \
printval_case(x, #x, 0, \
printval_case(x, #x, 1, \
printval_case(x, #x, 2, \
printval_case(x, #x, 3, \
/* etc... */ \
(void)0 )))) \
))) void printval() {} \
printval(); \
} while (0)
int main() {
printval(offsetof(struct A, a));
printval(offsetof(struct A, b));
}
編譯后,gcc 會輸出:
<source>:23:30: warning: 'printval' is deprecated: offsetof(struct A, a)=0 [-Wdeprecated-declarations]
<source>:24:30: warning: 'printval' is deprecated: offsetof(struct A, b)=1 [-Wdeprecated-declarations]
以類似的方式,您可以將值嵌入到可執行文件中(類似於 CMake 檢測編譯器內容的方式):
#include <stddef.h>
struct A {
char a;
char b;
} __attribute__((packed));
#define printval_case(x, xstr, y, ...) __builtin_choose_expr(x == y, xstr"="#y, __VA_ARGS__)
#define embedval(x) do { \
static const __attribute__((__used__)) const char unused[] = \
printval_case(x, #x, 0, \
printval_case(x, #x, 1, \
printval_case(x, #x, 2, \
printval_case(x, #x, 3, \
/* etc... */ \
(void)0 )))); \
} while (0)
int main() {
embedval(offsetof(struct A, a));
embedval(offsetof(struct A, b));
}
然后:
$ gcc file.c && strings ./a.out | grep offsetof
offsetof(struct A, b)=1
offsetof(struct A, a)=0
也許新的預處理步驟是可以接受的。 這可以作為一個單獨的步驟完成,不會影響您的生產二進制文件。
offsetdumper.sh
#!/bin/bash
#
# pre-process some source file(s), add a macro + main() and a file with rules
# describing the interesting symbos. Compile and run the result.
dumprulefile="$1"
shift
# Define your own macros, like OFFSET, in the "Here Document" below:
{
gcc -E "$@" && cat<<EOF
#define OFFSET(x,y) do { printf("%s::%s %zu\n", #x, #y, offsetof(x,y)); } while(0)
#include <stddef.h>
#include <stdio.h>
int main() {
EOF
cat "$dumprulefile"
echo '}'
} | g++ -x c - && ./a.out
rules
OFFSET(A,a);
OFFSET(A,b);
source.h
typedef struct {
char a;
char b;
} __attribute__((packed)) A;
例子:
$ ./offsetdumper.sh rules *.h
A::a 0
A::b 1
這有點脆弱,如果你的source.h
包含一個main
函數,它就不起作用,所以它可能需要一些修補來滿足你的需求。
一種可能的方法是將偏移量設置為數組的大小,然后將該數組的地址傳遞給期望不兼容指針的函數,以便打印類型:
static int a[offsetof(struct A, b)];
static void foo1(int *p) { (void)p; }
static void foo2(void) { foo1(&a); }
這打印:
x1.c: In function ‘foo2’:
x1.c:13:1: warning: passing argument 1 of ‘foo1’ from incompatible pointer type [enabled by default]
static void foo2(void) { foo1(&a); }
^
x1.c:12:13: note: expected ‘int *’ but argument is of type ‘int (*)[1]’
static void foo1(int *p) { (void)p; }
^
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.