我有一个案例,朋友将类型为"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) 请考虑以下情形:
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 API时的一个常见模式是在公共头中转发声明一些不透明的类型,这些类型传递给您的API方法,然后reinterpret_cast在翻译单元内转换为定义的C++类型(因此返回C++版本).
在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)
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLVMContext, LLVMContextRef)
Run Code Online (Sandbox Code Playgroud)
所以我们看到C API基本上接受一个指针LLVMOpaqueContext并将其转换为一个llvm::LLVMContext对象,以执行在其上调用的任何方法.
我的问题是:这不违反严格的别名规则吗?如果没有,为什么不呢?如果是这样,那么在公共接口边界上的这种抽象如何能够合法地实现呢?
在一个特定的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*>这样会因为类型别名而导致未定义的行为,并且memcpy在union不复制数据和分配额外空间的情况下无法使用浮点数/双数,这会失败在我的情况下,目的并且碰巧是昂贵的(并且在C++中不允许使用用于类型惩罚的联合).
可以假设浮动缓冲区已正确对齐以将其用于双精度.
我正在使用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组成工会?看起来这应该是网络中的一个很好的领域,我只是找不到任何有最佳实践的好例子.
此外,如果有人能够准确解释别名问题发生的位置,我会非常感激.
我正在尝试使用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_buf和outgoing_buf定义如下:
char incoming_buf[LIBIRC_DCC_BUFFER_SIZE];
char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];
Run Code Online (Sandbox Code Playgroud)
这似乎与我一直在研究的那个警告的其他例子略有不同.我宁愿修复问题而不是禁用严格别名检查.
有很多建议要使用工会 - 这个案例可能是一个合适的工会?
下面的代码通过一些位攻击执行快速反平方根操作.该算法可能是由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
我的问题的背景是网络编程.假设我想通过网络在两个程序之间发送消息.为简单起见,假设消息看起来像这样,字节顺序不是问题.我想找到一种正确,可移植且有效的方法来将这些消息定义为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) 阅读https://en.cppreference.com/w/cpp/language/reinterpret_cast我想知道哪些用例reinterpret_cast不是 UB 并在实践中使用?
上面的描述包含了许多将指针转换为其他类型然后再转换回来是合法的情况。但这似乎不太实用。通过指针访问对象reinterpret_cast大多是 UB,因为违反了严格别名(和/或对齐),除了通过char*/byte*指针访问之外。
一个有用的例外是将整数常量转换为指针并访问目标对象,这对于操作硬件寄存器(在 \xc2\xb5C 中)很有用。
\n谁能告诉我们在实践中使用的reinterpret_cast相关性的一些真实用例吗?
\nstrict-aliasing ×10
c++ ×7
c ×4
type-punning ×3
c++17 ×2
gcc ×1
gcc-warning ×1
llvm ×1
networking ×1
optimization ×1
portability ×1
sockets ×1