【问题标题】:Is there a way to determine a member offset at compile time?有没有办法在编译时确定成员偏移量?
【发布时间】:2014-01-26 20:55:32
【问题描述】:

我发现我在调试时花费了大量时间来尝试确定结构的成员偏移量。我想知道是否有一种快速方法可以在编译时确定大型结构中成员的偏移量。 (注意:我知道几种创建偏移量在给定范围内的编译时断言的方法,我可以对正确的值进行二进制搜索,但我正在寻找更有效的方法)。我正在使用 gcc 的最新版本来编译 C 代码。

【问题讨论】:

  • offsetof(structure, fieldname) 没有帮助?
  • @MarianV 为什么不回答这个问题(也许有一个使用示例)?
  • @eric_finn 好的。我不确定它是否在编译时有效。
  • “在编译时”是什么意思?编译器需要能够确定 offsetof 导致常量表达式(例如,您可以将其用于静态存储数组的大小);这是您可以获得的“在编译时”的正式定义最接近的东西。唯一可能的其他含义是“可用于预处理器#if 条件;这当然不适用于offsetof,因为预处理器不了解结构。
  • 嗯,也许我没有像我想象的那样解释这一点...我正在寻找一种确定偏移量的方法而不实际运行代码。加载和运行系统需要很长时间,而且我经常无法访问我正在调试的系统。我尝试像这样声明一个数组:char dbg[0-sizeof(offsetof(x,y)];,这会导致编译失败,但不会打印出实际的偏移量。我不确定我要问的是否可能,但我想我会把它扔在那里以防万一。

标签: c gcc


【解决方案1】:

offsetof 是你想要的,它编译时间。 C99 draft standard 部分 7.17 通用定义 段落 3 说:

offsetof(type, member-designator)

扩展为具有 size_t 类型的整数常量表达式,其值 这是以字节为单位的偏移量 [...]

上面链接的手册页有以下示例代码,演示了它的用法:

struct s {
    int i;
    char c;
    double d;
    char a[];
};

/* Output is compiler dependent */

printf("offsets: i=%ld; c=%ld; d=%ld a=%ld\n",
        (long) offsetof(struct s, i),
        (long) offsetof(struct s, c),
        (long) offsetof(struct s, d),
        (long) offsetof(struct s, a));
printf("sizeof(struct s)=%ld\n", (long) sizeof(struct s));

样本输出,可能会有所不同:

 offsets: i=0; c=4; d=8 a=16
 sizeof(struct s)=16

供参考常量表达式6.6部分中介绍常量表达式和段落2说:

可以在翻译期间而不是运行时计算常量表达式,并且 因此,可以在常数可能存在的任何地方使用。

更新

经过OP的澄清,我想出了一个使用-O0 -fverbose-asm -Sgrep的新方法,代码如下:

#include <stddef.h>

struct s {
        int i;
        char c;
        double d;
        char a[];
    };

int main()
{
    int offsetArray[4] = { offsetof(struct s, i ), offsetof( struct s, c ), offsetof(struct s, d ), offsetof(struct s, a )  } ;

    return 0 ;
}

构建使用:

gcc -x c -std=c99 -O0 -fverbose-asm  -S main.cpp && cat main.s | grep offsetArray

样本输出(live example):

movl    $0, -16(%rbp)   #, offsetArray
movl    $4, -12(%rbp)   #, offsetArray
movl    $8, -8(%rbp)    #, offsetArray
movl    $16, -4(%rbp)   #, offsetArray

【讨论】:

  • 只是为了让 OP 清楚,因为他显然从未听说过这个:这是一个结构 mystruct 的示例用法,您想知道成员 x 的偏移量:@ 987654334@
  • 谢谢,我听说过 offsetof,但是它不符合我的需要,因为我不知道在编译时输出值的方法。这是我正在调试的远程(嵌入式)系统,虽然我有崩溃日志,但我无法访问系统本身。
  • @John 离开你在主要问题中的评论并按照我上面的例子你可以这样做int arr[4] = { offsetof(struct s, i ), offsetof( struct s, c ), offsetof(struct s, d ), offsetof(struct s, a ) } ; ...如果这还不够,那么你需要充实细节更可能需要一些代码来澄清。
  • 有趣——我想我可以做一个数组的 objdump,并以这种方式打印值。我想我偶然发现了其他东西——如果由于堆栈过大而导致编译失败,编译器会告诉你堆栈大小是多少……我现在正在尝试。
  • @John 好的,你可以使用-O0 -fverbose-asm -S,然后你可以使用 grep,我会添加到我的答案中。
【解决方案2】:

好的,在这里回答我自己的问题:注意:我希望在编译时确定偏移量,也就是说,我不想运行代码(我也可以只编译我需要的文件,而不是整个系统):有兴趣的可以剪切和粘贴以下内容:

#include <stddef.h>

#define offsetof_ct(structname, membername) \
void ___offset_##membername ## ___(void) { \
        volatile char dummy[10000 + offsetof(structname, membername) ]; \
        dummy[0]=dummy[0]; \
}

struct x {
        int a[100];
        int b[20];
        int c[30];
};

offsetof_ct(struct x,a);
offsetof_ct(struct x,b);
offsetof_ct(struct x,c);

然后运行:

~/tmp> gcc tst.c -Wframe-larger-than=1000
tst.c: In function ‘___offset_a___’:
tst.c:16:1: warning: the frame size of 10000 bytes is larger than 1000 bytes
tst.c: In function ‘___offset_b___’:
tst.c:17:1: warning: the frame size of 10400 bytes is larger than 1000 bytes
tst.c: In function ‘___offset_c___’:
tst.c:18:1: warning: the frame size of 10480 bytes is larger than 1000 bytes
/usr/lib/gcc/x86_64-redhat-linux/4.5.1/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: ld returned 1 exit status

然后我减去 10000 得到偏移量。注意:我尝试在文件中添加#pragma GCC diagnostic warning "-Wframe-larger-than=1000",但它不喜欢它,所以必须在命令行中指定它。

约翰

【讨论】:

    【解决方案3】:

    应该这样做:

    #define OFFSETOF(T, m) \
      (size_t) (((char *) &(((T*) NULL)->m)) - ((char *) ((T*) NULL)))
    

    这样称呼它:

    size_t off = OFFSETOF(struct s, c);
    

    【讨论】:

    • 为什么要重新发明轮子? offsetof 是标准的,在&lt;stddef.h&gt; 中定义。
    • @KeithThompson:如果你一个人在树林里,只是为了展示如何实现这一点。
    • @KeithThompson 我认为这是值得商榷的,但可以说,许多人认为所有常见的 offsetof 宏都没有很好地定义,除非它们是实现的一部分。
    • 对不起,这没有任何意义。如果你有一个符合标准的 C 实现,你有offsetof。您的 OFFSETOF 宏虽然可能有效,但表现出未定义的行为。 (特定的offsetof 实现也可能这样做,但它必须适用于特定的实现。)除非您正在编写自己的&lt;stddef.h&gt;,否则没有多大意义。
    • @ShafikYaghmour:没错。
    猜你喜欢
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2021-09-23
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多