GCC 4.4.3常量表达式错误的偏移.我应该如何解决这个问题

GCC 4.4.3 offsetof constant expression bug. How should I work around this?

本文关键字:何解决 解决 问题 我应该 表达式 常量 错误 GCC      更新时间:2023-10-16

我有一个结构,它包含一个静态常量表达式,该表达式使用stddef.h 中定义的偏移量宏


struct SomeType {
    int m_member;
};
static const size_t memberOffset = offsetof(SomeType, m_member);

在GCC 4.4.3中(我使用的是Androids NDK r7),这会产生以下错误:


arm-linux-androideabi-g++ -MMD -MP -MF ./obj/local/armeabi-v7a/... -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__  -Wno-psabi -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -fno-exceptions -fno-rtti -O2 -fomit-frame-pointer -fstrict-aliasing -funswitch-loops -finline-limit=300 - -I/Users/Byron/bin/android-ndk-r7/sources/cxx-stl/system/include -
 -Wa,--noexecstack   -O0 -g -w -D_ANDROID   -I/blah/bin/android-ndk-r7/platforms/android-14/arch-arm/usr/include -c  
/MyFile.h:330: error: '->' cannot appear in a constant-expression
/MyFile.h:330: error: '&' cannot appear in a constant-expression

这看起来像是一个编译器错误。有人能解决这个问题吗?我在GCC 3.4上找到了这种性质的bug的参考,但没有更新的版本。hmmm

标准

在C++98标准中,中有一些信息

C.2.4.1宏offsetof类型成员指示符)[diff.offsetof]

<cstddef>中定义的宏offsetof接受此International标准§18.1描述了该变更。

(C.2.4.1在内容中显示offsetof,所以我先去了那里。)和:

§18.1类型18语言支持库

¶5宏offsetof接受本国际标准中的一组受限类型自变量类型应为POD结构或POD联合(第9条)。将offsetof宏应用于字段的结果是静态数据成员,或者函数成员未定义。

为了进行比较,C99标准规定:

 offsetof(type, member-designator)

其扩展为具有类型size_t的整数常量表达式其是以字节为单位的偏移量)到结构成员(由成员指示符指定),从其结构的开始(由类型指定)。类型成员指示符应使得给定

static type t;

则表达式CCD_ 7评估为地址常数。(如果指定的成员是位字段,行为未定义。)


您的代码

在我看来,您的代码符合C++和C标准的要求。

当我在RedHat(RHEL 5)上使用G++4.1.2和GCC 4.5.1时,此代码的编译不会受到-Wall -Wextra选项的影响:

#include <cstddef>
struct SomeType {
    int m_member;
};
static const int memberOffset = offsetof(SomeType, m_member);

它还使用#include <stddef.h>和GCC编译器进行编译(如果我在宏调用中使用struct SomeType)。

我想知道-我有错误,直到我包括<cstddef>。。。你包括那个吗?当然,我还在声明中添加了类型int

假设您的代码中没有任何错误,在我看来,您可能在平台上的<cstddef>(或<stddef.h>)标头中发现了一个错误。您不应该得到错误,而基于Linux的G++似乎证实了这一点。

解决方法

您需要查看offsetof()是如何在系统头中定义的。然后,你可能会重新定义它,以避免遇到问题。

假设您以某种方式识别出损坏的系统并执行#define BROKEN_OFFSETOF_MACRO(或将-DBROKEN_OFFSETOF_MACRO添加到命令行),您可能可以使用类似的方法。

#include <cstddef>
#ifdef BROKEN_OFFSETOF_MACRO
#undef offsetof
#define offsetof(type, member)   ((size_t)((char *)&(*(type *)0).member - 
                                           (char *)&(*(type *)0)))
#endif /* BROKEN_OFFSETOF_MACRO */
struct SomeType {
    int m_member;
};
static const int memberOffset = offsetof(SomeType, m_member);

由于两个地址之间的差为ptrdiff_t,并且offset()宏被定义为返回size_t,因此存在size_t强制转换。这个宏很难看,但这就是为什么它通常隐藏在系统头中,你不必看它的所有可怕之处。但是,当其他一切都失败时,你必须做任何必要的事情。

我知道有一次,大约在1990年,我遇到了一个C编译器,它不允许0,但允许1024。当然,分布式<stddef.h>标头使用了0,所以我通过在持续时间内将0更改为1024(两次)来"修复"它(直到我在更好的机器上获得更好的编译器)。

offsetof()必须使用指针算术进行定义。GCC可能不喜欢常量表达式中的这种情况,因为理论上指针可能会改变,所以它是非const。变通办法可能是让它成为没有conststatic int