C++ 是';抵消';宏来自<;标准差速>;调用未定义的行为?

C++ 是';抵消';宏来自<;标准差速>;调用未定义的行为?,c++,undefined-behavior,offsetof,C++,Undefined Behavior,Offsetof,MSVC实现的示例: #define offsetof(s,m) \ (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) // ^^^^^^^^^^^ 定义(s,m)的偏移量\ (尺寸t)和重新解释铸件(((s*)0)->m)) //

MSVC实现的示例:

#define offsetof(s,m) \
    (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))
//                                                   ^^^^^^^^^^^
定义(s,m)的偏移量\ (尺寸t)和重新解释铸件(((s*)0)->m)) // ^^^^^^^^^^^
可以看出,它取消了对空指针的引用,空指针通常调用未定义的行为。这是规则的例外还是正在发生的事情?

不,这不是未定义的行为。表达式在运行时解析


注意,它从空指针获取成员
m
的地址。它不是取消对空指针的引用。

如果语言标准说“未定义的行为”,任何给定的编译器都可以定义该行为。标准库中的实现代码通常依赖于此。因此有两个问题:

(1)是关于C++标准的代码UB?< /P> 这是一个非常困难的问题,因为众所周知,C++98/03标准几乎没有在规范性文本中明确指出,通常情况下,取消对空指针的引用是错误的。它由

typeid
的异常所暗示,在该异常中它不是UB

您可以明确地说,使用非POD类型的
offsetof
是非常重要的

(2) 代码是否与为其编写的编译器相关

不,当然不是

编译器供应商针对给定编译器的代码可以使用该编译器的任何功能

Cheers&hth.,

标准库的实现不适用于“未定义行为”的概念,无论它是宏、函数还是其他任何东西

一般情况下,标准库不应被看作是用C++语言实现的。这也适用于标准头文件。标准库应该符合其外部规范,但其他所有内容都是实现细节,不受语言的所有和任何其他要求的约束。标准库应该总是被认为是用某种“内部”语言实现的,它可能与C++或C非常相似,但仍然不是C++或C.<
换句话说,您引用的宏不会产生未定义的行为,只要它是标准库中定义的宏的
偏移量。但是,如果在代码中执行完全相同的操作(如以完全相同的方式定义自己的宏),则确实会导致未定义的行为。“Quod licet Jovi,non licet bovi”。

这基本上等同于询问这是否是UB:

s* p = 0;
volatile auto& r = p->m;
显然,不会生成对
r
目标的内存访问,因为它是
易失性的
,并且禁止编译器生成对
易失性
变量的虚假访问。但是
*s
不是易变的,因此编译器可能会生成对它的访问。运算符的地址和转换为引用类型都不会根据标准创建未计算的上下文

因此,我不认为有任何理由使用
volatile
,我同意其他人的观点,即根据标准,这是未定义的行为。当然,任何编译器都可以在标准未指定或未定义其实现的情况下定义行为

最后,
[dcl.ref]
一节中的一条注释说

特别是,空引用不能存在于定义良好的程序中,因为创建此类引用的唯一方法是将其绑定到通过取消引用空指针获得的“对象”,这会导致未定义的行为


当C标准指定某些操作调用未定义的行为时,这通常并不意味着这些操作被禁止,而是意味着实现可以自由地指定后续行为,或者不指定它们认为合适的行为。因此,在标准要求定义行为的情况下,实现可以自由地执行此类操作,前提是实现可以保证这些操作的行为与标准要求的一致。例如,考虑STRCPY的以下实现:

char *strcpy(char *dest, char const *src)
{
  ptrdiff_t diff = dest-src-1;
  int ch;
  while((ch = *src++) != 0)
    src[diff] = ch;
  return dest;
}
如果
src
dest
是不相关的指针,
dest-src
的计算将产生未定义的行为。然而,在某些平台上,
char*
ptrdiff_t
之间的关系是这样的:给定任何
char*p1,p2
,计算
p1+(p2-p1)将始终等于
p2
。在保证这一点的平台上,上述strcpy的实现是合法的(并且在某些这样的平台上可能比任何可能的替代方案都要快)。然而,在其他一些平台上,这样的函数可能总是失败,除非两个字符串都是同一个已分配对象的一部分


同样的原则也适用于宏的偏移量。如果编译器的指针算法模型允许在空指针上使用
->
运算符获得所需的
偏移量,则编译器不需要提供任何方法来获得与
偏移量
行为等效的行为(实际使用该宏除外),然后它的
偏移量
宏就可以做到这一点。如果编译器不支持在指向该类型实例的合法指针之外的其他对象上使用
->
,那么它可能需要定义一个可以计算字段偏移量的内部变量,并定义
偏移量以使用该宏。重要的不是标准定义了使用标准库宏和函数执行的行为,而是实现了确保这些宏和函数的行为符合要求。

< P> >在代码< S/<代码>结构中,如果<代码> M < /C> >偏移0,则C++中不是未定义的行为。以及在某些其他情况下。根据(我的重点):

一元