就在几周前,我了解到C++标准有一个严格的别名规则。基本上,我的问题是关于位移的 -- 我想要一次性将32或64位的处理器寄存器加载,并执行4/8字节的位移以最大化性能,而不是逐个位移每个字节。
这是我想要避免的代码:
unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };
for (int i = 0; i < 3; ++i)
{
buffer[i] <<= 4;
buffer[i] |= (buffer[i + 1] >> 4);
}
buffer[3] <<= 4;
相反,我想使用类似的东西:
unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };
unsigned int *p = (unsigned int*)buffer; // unsigned int is 32 bit on my platform
*p <<= 4;
有人在评论中指出,我的解决方案违反了C++别名规则(因为p是int *
类型而buffer是char *
类型,并且我正在对p进行引用以执行移位操作。(请忽略可能的对齐和字节顺序问题--我在此片段之外处理这些问题)我非常惊讶地了解到了严格别名规则,因为我经常操作来自缓冲区的数据,将其从一种类型转换为另一种类型,并从未遇到任何问题。进一步调查发现,我使用的编译器(MSVC)不强制执行严格别名规则,由于我只在业余时间作为爱好者开发gcc / g ++,所以可能还没有遇到这个问题。
然后我问了一个与严格别名规则和C ++的放置新运算符有关的问题:
IsoCpp.org提供了一个有关放置new的FAQ,并提供了以下代码示例:
#include <new> // Must #include this to use "placement new"
#include "Fred.h" // Declaration of class Fred
void someCode()
{
char memory[sizeof(Fred)]; // Line #1
void* place = memory; // Line #2
Fred* f = new(place) Fred(); // Line #3 (see "DANGER" below)
// The pointers f and place will be equal
// ...
}
这个例子足够简单,但我在想,“如果有人调用f
的方法--例如f->talk()
会怎么样?此时我们将对指向与memory
相同的内存位置的f
进行解引用(类型为char*
)。我已经在许多地方读到类型为char*
的变量有例外,可以别名任何类型,但我认为它不是“双向街道”--这意味着char*
可以别名(读/写)任何类型T
,但只有当T
本身是char*
类型时,类型T
才能用于别名char*
。当我打字时,这对我来说毫无意义,所以我倾向于相信我的初始(bit shifting example)违反了严格别名规则的说法是错误的。
请问有人能解释一下什么是正确的吗?尽管已经阅读了许多网站和SO帖子,我仍然试图理解什么是合法的,什么不合法。
谢谢
f
的成员函数是未定义行为,那么这会使得放置new变得无用,不是吗? - Barrymemory
的类型不是char*
。在第二行,数组会转化为指针,但这并不意味着memory
是一个指针。即使memory
是一个指针,它所指向的内存位置的类型也将是char
而不是char*
。 - davmacf->talk()
是否可行;我认为删除所有前置内容(“So then”之前的内容)会使问题更加清晰。 - M.M