标签: strict-aliasing

为什么GCC和Clang不进行混叠优化?

我有一个案例,朋友将类型为"Base"的非基类对象强制转换为类类型对象"Derived",其中"Derived"是"Base"的派生类,只添加函数,但没有数据.在下面的代码中,我确实x向派生类添加了一个数据成员

struct A {
  int a;
};

struct B : A {
  // int x;
  int x;
};

A a;

int g(B *b) {
   a.a = 10;
   b->a++;
   return a.a;
}
Run Code Online (Sandbox Code Playgroud)

通过严格的别名分析,GCC(也是Clang)总是返回10,而不是11,因为b永远不会指向a明确定义的代码.但是,如果我删除B::x(因为实际上是在我朋友的代码的情况下),GCC的输出汇编代码并没有优化的回归访问a.a并重新加载从内存中值.因此我的朋友的代码g在GCC(按照他的意图)调用"有效",即使我认为它仍然有未定义的行为

g((B*)&a);
Run Code Online (Sandbox Code Playgroud)

因此,在基本相同的两种情况下,GCC优化了一种情况并且没有优化另一种情况.是因为b可以合法地指出a?或者是因为GCC只是想破坏现实世界的代码?


我测试了答案

如果删除B :: x,那么B符合9p7中对标准布局类的要求,并且访问变得非常明确,因为这两种类型是布局兼容的,9.2p17.

有两个布局兼容的枚举

enum A : int { X, Y };
enum B : int { Z …
Run Code Online (Sandbox Code Playgroud)

c++ optimization gcc strict-aliasing compiler-optimization

27
推荐指数
2
解决办法
1406
查看次数

std :: byte指针是否与char*具有相同的别名含义?

C++(和C)严格别名规则包括a char*unsigned char*可以别名任何其他指针.

AFAIK没有类似的规则uint8_t*.

因此我的问题是:std::byte指针的别名规则是什么?

C++参考目前只是指定:

与字符类型(char,unsigned char,signed char)一样,它可用于访问其他对象占用的原始内存(对象表示),但与这些类型不同,它不是字符类型,也不是算术类型.

c++ strict-aliasing c++17

27
推荐指数
1
解决办法
1213
查看次数

`reinterpret_cast`是一个'T*`到'T(*)[N]`是不确定的行为?

请考虑以下情形:

std::array<int, 8> a;
auto p = reinterpret_cast<int(*)[8]>(a.data());
(*p)[0] = 42;
Run Code Online (Sandbox Code Playgroud)

这是未定义的行为吗?我觉得是这样的.

  • a.data()返回一个int*,不一样的int(*)[8]

  • cppreference上的类型别名规则似乎表明它无效reinterpret_cast

  • 作为程序员,我知道指向的内存位置a.data()是一个8 int对象数组

是否有任何我失踪的规则使这个reinterpret_cast有效?

c++ strict-aliasing language-lawyer reinterpret-cast c++17

25
推荐指数
2
解决办法
1298
查看次数

为您的C++库提供C API并严格别名

提供C API时的一个常见模式是在公共头中转发声明一些不透明的类型,这些类型传递给您的API方法,然后reinterpret_cast在翻译单元内转换为定义的C++类型(因此返回C++版本).

以LLVM为例:

Types.h中,声明了这个typedef:

typedef struct LLVMOpaqueContext *LLVMContextRef;
Run Code Online (Sandbox Code Playgroud)

LLVMOpaqueContext 未在项目中的任何其他位置引用.

Core.h中,声明了以下方法:

LLVMContextRef LLVMContextCreate(void);
Run Code Online (Sandbox Code Playgroud)

这是在Core.cpp中定义的:

LLVMContextRef LLVMContextCreate() {
  return wrap(new LLVMContext());
}
Run Code Online (Sandbox Code Playgroud)

wrap(和unwrap)由CBindingWrapping.h中的宏定义:

#define DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ty, ref)     \
  inline ty *unwrap(ref P) {                            \
    return reinterpret_cast<ty*>(P);                    \
  }                                                     \
                                                        \
  inline ref wrap(const ty *P) {                        \
    return reinterpret_cast<ref>(const_cast<ty*>(P));   \
}
Run Code Online (Sandbox Code Playgroud)

并在LLVMContext.h中使用:

DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLVMContext, LLVMContextRef)
Run Code Online (Sandbox Code Playgroud)

所以我们看到C API基本上接受一个指针LLVMOpaqueContext并将其转换为一个llvm::LLVMContext对象,以执行在其上调用的任何方法.

我的问题是:这不违反严格的别名规则吗?如果没有,为什么不呢?如果是这样,那么在公共接口边界上的这种抽象如何能够合法地实现呢?

c c++ llvm strict-aliasing

25
推荐指数
1
解决办法
1759
查看次数

重复使用浮动缓冲区,但没有未定义的行为

在一个特定的C++函数中,我碰巧有一个指向浮点缓冲区的指针,我想暂时用它来存储一半的双精度数.有没有一种方法可以使用此缓冲区作为临时空间来存储双打,这也是标准允许的(即,不是未定义的行为)?

总之,我想这样:

void f(float* buffer)
{
  double* d = reinterpret_cast<double*>(buffer);
  // make use of d
  d[i] = 1.;
  // done using d as scratch, start filling the buffer
  buffer[j] = 1.;
}
Run Code Online (Sandbox Code Playgroud)

据我所知,没有简单的方法可以做到这一点:如果我理解正确,reinterpret_cast<double*>这样会因为类型别名而导致未定义的行为,并且memcpyunion不复制数据和分配额外空间的情况下无法使用浮点数/双数,这会失败在我的情况下,目的并且碰巧是昂贵的(并且在C++中不允许使用用于类型惩罚的联合).

可以假设浮动缓冲区已正确对齐以将其用于双精度.

c++ strict-aliasing type-punning

25
推荐指数
2
解决办法
945
查看次数

如何投射sockaddr_storage并避免违反严格别名规则

我正在使用Beej的网络指南,并遇到了一个别名问题.他提出了一个函数来返回特定结构的IPv4或IPv6地址:

1  void *get_in_addr( struct sockaddr *sa )
2  {
3      if (sa->sa_family == AF_INET)
4        return &(((struct sockaddr_in*)sa)->sin_addr);
5      else
6        return &(((struct sockaddr_in6*)sa)->sin6_addr);
7  }
Run Code Online (Sandbox Code Playgroud)

这会导致GCC 在第3行为sa吐出严格别名错误.据我所知,这是因为我这样调用这个函数:

struct sockaddr_storage their_addr;
...
inet_ntop(their_addr.ss_family,
          get_in_addr((struct sockaddr *)&their_addr),
          connection_name,
          sizeof connection_name);
Run Code Online (Sandbox Code Playgroud)

我猜测别名与their_addr变量是类型的事实有关,sockaddr_storage而另一个不同类型的指针指向同一个内存.

就是要解决这个症结的最佳方式sockaddr_storage,sockaddr_in以及sockaddr_in6组成工会?看起来这应该是网络中的一个很好的领域,我只是找不到任何有最佳实践的好例子.

此外,如果有人能够准确解释别名问题发生的位置,我会非常感激.

c sockets strict-aliasing pointer-aliasing

24
推荐指数
1
解决办法
1万
查看次数

修复取消引用类型惩罚指针将破坏严格别名

我正在尝试使用GCC编译特定程序时修复两个警告.警告是:

警告:解除引用类型惩罚指针将破坏严格别名规则[-Wstrict-aliasing]

而这两个罪魁祸首是:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
Run Code Online (Sandbox Code Playgroud)

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
Run Code Online (Sandbox Code Playgroud)

incoming_bufoutgoing_buf定义如下:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];
Run Code Online (Sandbox Code Playgroud)

这似乎与我一直在研究的那个警告的其他例子略有不同.我宁愿修复问题而不是禁用严格别名检查.

有很多建议要使用工会 - 这个案例可能是一个合适的工会?

c strict-aliasing type-punning

24
推荐指数
3
解决办法
5万
查看次数

什么是将一个浮点数类型化为int的正确方法,反之亦然?

下面的代码通过一些位攻击执行快速反平方根操作.该算法可能是由Silicon Graphics在1990年代早期开发的,它也出现在Quake 3中. 更多信息

但是我从GCC C++编译器收到以下警告:解除引用类型惩罚指针将破坏严格别名规则

我应该使用static_cast,reinterpret_cast还是dynamic_cast在这种情况下使用?

float InverseSquareRoot(float x)
{
    float xhalf = 0.5f*x;
    int32_t i = *(int32_t*)&x;
    i = 0x5f3759df - (i>>1);
    x = *(float*)&i;
    x = x*(1.5f - xhalf*x*x);
    return x;
}
Run Code Online (Sandbox Code Playgroud)

c++ strict-aliasing gcc-warning undefined-behavior type-punning

24
推荐指数
4
解决办法
6830
查看次数

将缓冲区解释为结构的正确,可移植的方法

我的问题的背景是网络编程.假设我想通过网络在两个程序之间发送消息.为简单起见,假设消息看起来像这样,字节顺序不是问题.我想找到一种正确,可移植且有效的方法来将这些消息定义为C结构.我知道有四种方法:显式铸造,通过联合铸造,复制和编组.

struct message {
    uint16_t logical_id;
    uint16_t command;
};
Run Code Online (Sandbox Code Playgroud)

显式铸造:

void send_message(struct message *msg) {
    uint8_t *bytes = (uint8_t *) msg;
    /* call to write/send/sendto here */
}

void receive_message(uint8_t *bytes, size_t len) {
    assert(len >= sizeof(struct message);
    struct message *msg = (struct message*) bytes;
    /* And now use the message */
    if (msg->command == SELF_DESTRUCT)
        /* ... */
}
Run Code Online (Sandbox Code Playgroud)

我的理解是send_message不违反别名规则,因为byte/char指针可以为任何类型设置别名.但是,相反的情况并非如此,因此receive_message违反了别名规则,因此具有未定义的行为.

通过联盟投射:

union message_u {
    struct message m;
    uint8_t bytes[sizeof(struct message)];
};

void receive_message_union(uint8_t *bytes, size_t …
Run Code Online (Sandbox Code Playgroud)

c networking portability strict-aliasing

24
推荐指数
2
解决办法
9395
查看次数

使用reinterpret_cast 的示例不会触发UB

阅读https://en.cppreference.com/w/cpp/language/reinterpret_cast我想知道哪些用例reinterpret_cast不是 UB 并在实践中使用?

\n

上面的描述包含了许多将指针转换为其他类型然后再转换回来是合法的情况。但这似乎不太实用。通过指针访问对象reinterpret_cast大多是 UB,因为违反了严格别名(和/或对齐),除了通过char*/byte*指针访问之外。

\n

一个有用的例外是将整数常量转换为指针并访问目标对象,这对于操作硬件寄存器(在 \xc2\xb5C 中)很有用。

\n

谁能告诉我们在实践中使用的reinterpret_cast相关性的一些真实用例吗?

\n

c++ strict-aliasing undefined-behavior reinterpret-cast

24
推荐指数
2
解决办法
2296
查看次数