C++ 将char*严格别名为更广泛的类型,而不具有未定义的行为

C++ 将char*严格别名为更广泛的类型,而不具有未定义的行为,c++,C++,我对这是否是一个严格的别名冲突或调用未定义的行为感到困惑。很多人都告诉我这不是违反行为,没有UB,但是从阅读C++规范听起来好像做任何事,一个类型的铸造指针(除非它是CV铸造或铸造成兼容类型或字符)是UB。 #定义交换(x)((x)>8))) char*foo(char*data,大小\u t len16){ int align=重新解释铸件(数据)%尺寸(uint16 t); 如果(align==0){ uint16*data16=重新解释铸件(数据); 对于(大小i=0;i

我对这是否是一个严格的别名冲突或调用未定义的行为感到困惑。很多人都告诉我这不是违反行为,没有UB,但是从阅读C++规范听起来好像做任何事,一个类型的铸造指针(除非它是CV铸造或铸造成兼容类型或字符)是UB。
#定义交换(x)((x)>8)))
char*foo(char*data,大小\u t len16){
int align=重新解释铸件(数据)%尺寸(uint16 t);
如果(align==0){
uint16*data16=重新解释铸件(数据);
对于(大小i=0;i

(这是一个稍微做作的示例;实际上,
SWAP
可能是需要
uint16\t
的第三方函数)

是因为我们检查了对齐方式,对类型的大小很有信心,而不关心结尾,所以故事发生了变化吗?我想,剩下的问题是优化器消除死代码

如果这是非法的,您如何有效地将
char
缓冲区(例如来自文件)解释为其预期类型(例如
int16
s)?我很熟悉通过联合进行强制转换,但我并不认为这有什么不同(请参阅,除了告诉编译器它不能消除死代码之外)

任何取消引用类型转换指针的操作(除非它只是cv转换或转换为兼容类型或字符)都是UB

这是不对的

任何指针都可以强制转换到
char*
void*
并返回。只有当原始指针的类型与取消引用指针时使用的指针类型不同时,它才是UB。甚至还有例外。请参阅示例

很多人告诉我这不是违法行为,也没有UB

那些人错了。这个代码:

uint16_t* data16 = reinterpret_cast<uint16_t*>(data);
或:

但除此之外,这是UB。非UB的方法是:

uint16_t data16;
memcpy(&data16, data, sizeof(data16));
许多编译器都会将其视为一个
重新解释\u cast



也就是说,大量的网络代码与你所做的一样,如果编译器优化了这些代码,使其无法达到你所希望的效果,那么论坛上就会有很多愤怒的消息在街上出现。

为什么要投否决票?请留下评论。好的,好的,但是如果原始指针是
char*
然后不能将其重新解释为例如
uint16\u t
?或者我们可以说是“原始指针”一个
uint16\u t
是因为另一个可执行文件编写了
uint16\u t
s吗?@ZachB,你说的是通过网络接收的数据还是从文件中读取的数据?两者都是,但我们知道尾数,如果这就是你想要的。@ZachB,那是正确的。如果数据是以相同的尾数接收或读取的,那么你应该是好的。谢谢r有用的回答。Re:非UB方式,看起来g++确实将其视为对-Os、-O1和-O2的重新解释,但该方法似乎可以防止使用-O3展开循环(如果这是实际的deopt,则未进行测试)
uint16_t p = 42;
foo(reinterpret_cast<char*>(&p), 2); // now, we're ok
char data[64];
new (data) uint16_t{42};
foo(data, 2); // also ok, though with C++17 you'll have to use std::launder
uint16_t data16;
memcpy(&data16, data, sizeof(data16));