简体   繁体   English

将指向typed / size enum的指针转换为指向底层类型的指针是否安全?

[英]Is it safe to convert a pointer to typed/sized enum to a pointer to the underlying type?

The following code: 以下代码:

void f(const uint8_t* a) {}  // <- this is an external library function
enum E : uint8_t { X, Y, Z };

int main(void) {
  E e = X;
  f(&e);  // <- error here
}

Produces the following error: 产生以下错误:

/tmp/c.cc:10:3: error: no matching function for call to 'f'
  f(&e);
  ^
/tmp/c.cc:5:6: note: candidate function not viable: no known conversion from 'E *' to 'const uint8_t *' (aka 'const unsigned char *') for 1st argument
void f(const uint8_t* e) { }

Which is surprising to me because I thought the : uint8_t in the enum's definition would mean they are necessarily represented with that underlying type. 这对我来说是令人惊讶的,因为我认为: uint8_t enum定义中的: uint8_t意味着它们必须用底层类型表示。 I can easily get around this with a cast: 我可以通过演员轻松解决这个问题:

f((uint8_t*)&e);

which I don't mind too much, but given that it's an error to omit it, is this always safe or is the : uint8_t not providing the guarantees I think it is? 我不介意太多,但鉴于省略它是一个错误,这是永远安全还是: uint8_t不提供我认为的保证?

It is indeed safe (although I'm not a language-lawyer): What's stored in memory is a uint8_t , and that's what you'll be pointing at. 它确实是安全的(虽然我不是语言律师):存储在内存中的是uint8_t ,这就是你要指出的内容。 However, if f() were to take a pointer to a non-const uint8_t , then it could potentially change the value to something that's not explicitly defined as one of the E enum values. 但是,如果f()要指向非const uint8_t ,那么它可能会将值更改为未明确定义为E enum值之一的值。 (Edit:) While this is apparently allowed by the C++ standard, it is surprising to many (see discussion in the comments below on this point), and I would encourage you to ensure it does not happen. (编辑:)虽然这显然是C ++标准所允许的,但对于许多人来说这是令人惊讶的(参见下面关于这一点的评论中的讨论),我鼓励你确保它不会发生。

... but as others suggest, you're not getting the error because of your notion of safety, but because implicit conversions aren't performed between pointed-to types. ...但正如其他人所说,由于你的安全概念,你没有得到错误,而是因为在指向类型之间不执行隐式转换。 You can pass an E to a function taking a uint8_t , but not an E * to a function taking a uint8_t * ; 您可以将一个E传递给一个函数,该函数将uint8_t ,而不是E *传递给一个取uint8_t *的函数; that would be - according to the language committee and in my opinion as well - an overly cavalier attitude to pointer types. 那将是 - 根据语言委员会和我认为 - 对指针类型过于傲慢的态度。

Afaik this is only legal by accident: Afaik这只是偶然合法的:

What you are doing is performing a reinterpret_cast and I'm assuming f is dereferencing that pointer internally. 你正在做的是执行reinterpret_cast ,我假设f在内部取消引用该指针。 This is only legal in a very restricted set of cases and while not normative, cppreference.com gives a good overview of those: 这在一组非常有限的案例中是合法的,虽然不是规范性的,但cppreference.com给出了很好的概述:

When a pointer or reference to object whose dynamic type is DynamicType is reinterpret_cast (or C-style cast) to a pointer or reference to object of a different type AliasedType, the cast always succeeds, but the resulting pointer or reference may only be used to access the object if one of the following is true: 当动态类型为DynamicType的对象的指针或引用是reinterpret_cast(或C样式转换)为指针或对不同类型AliasedType的对象的引用时,转换总是成功,但结果指针或引用可能仅用于如果满足以下条件之一,则访问该对象:

  • AliasedType is (possibly cv-qualified) DynamicType AliasedType是(可能是cv限定的)DynamicType

  • AliasedType and DynamicType are both (possibly multi-level, possibly cv-qualified at each level) pointers to the same type T (since C++11) AliasedType和DynamicType都是(可能是多级,可能在每个级别的cv限定)指向相同类型T的指针(自C ++ 11以来)

  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType AliasedType是DynamicType的(可能是cv限定的)有符号或无符号变体

  • AliasedType is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to obtain a usable pointer to a struct or union given a pointer to its non-static member or element. AliasedType是一种聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括,递归地,包含联合的子聚合和非静态数据成员的元素):这样可以安全地获取给定指向其非静态成员或元素的指针的结构或联合的可用指针。

  • AliasedType is a (possibly cv-qualified) base class of DynamicType and DynamicType is a standard-layout class that has has no non-static data members, and AliasedType is its first base class. AliasedType是DynamicType的(可能是cv限定的)基类,DynamicType是没有非静态数据成员的标准布局类,AliasedType是它的第一个基类。

  • AliasedType is char, unsigned char, or std::byte: this permits examination of the object representation of any object as an array of bytes. AliasedType是char,unsigned char或std :: byte:这允许将任何对象的对象表示检查为字节数组。

If AliasedType does not satisfy these requirements, accessing the object through the new pointer or reference invokes undefined behavior. 如果AliasedType不满足这些要求,则通过新指针或引用访问对象会调用未定义的行为。 This is known as the strict aliasing rule and applies to both C++ and C programming languages. 这称为严格别名规则,适用于C ++和C编程语言。

None of those cases includes casting to a pointer to an enum's underlying type! 这些情况都不包括转换为指向枚举的基础类型的指针!

However: 然而:
Casting to a pointer to unsigned char* and dereferencing it is always legal and on most platforms uint8_t is just a typedef for that. 转换为指向unsigned char*的指针并取消引用它总是合法的,在大多数平台上, uint8_t只是一个typedef。 So it is ok in this case, but wouldn't be if the underlying type eg would be a uint16_t . 所以在这种情况下是可以的,但如果基础类型例如是uint16_t不会。

That being said, I would not be surprised to hear that most compilers would allow such usage, even if the standard does not. 话虽如此,听到大多数编译器允许这样的使用,即使标准没有这样,我也不会感到惊讶。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM