C++ 使用常量变量可以避免别名问题吗
我的公司使用一个消息服务器,它将消息获取到C++ 使用常量变量可以避免别名问题吗,c++,constants,alias,reinterpret-cast,strict-aliasing,C++,Constants,Alias,Reinterpret Cast,Strict Aliasing,我的公司使用一个消息服务器,它将消息获取到const char*中,然后将其强制转换为消息类型 auto bar = reinterpret_cast<const MessageJ*>(foo); 问了我之后,我开始担心这件事。我不知道消息服务器中有任何不良行为。const变量是否可能不会产生别名问题 例如,假设foo在MessageServer中以以下方式之一定义: 作为参数:void MessageServer(const char*foo) 或者作为MessageServer
const char*
中,然后将其强制转换为消息类型
auto bar = reinterpret_cast<const MessageJ*>(foo);
问了我之后,我开始担心这件事。我不知道消息服务器中有任何不良行为。const
变量是否可能不会产生别名问题
例如,假设foo在MessageServer
中以以下方式之一定义:
void MessageServer(const char*foo)
MessageServer
顶部的const变量:const char*foo=PopMessage()代码>
MessageServer
是一个巨大的函数,但它从不向foo
分配任何内容,但是在MessageServer
的逻辑中的1点,foo
将转换为所选的消息类型
auto bar = reinterpret_cast<const MessageJ*>(foo);
我们是说这会使它合法吗
MessageX* foo = new MessageX;
const auto temp = reinterpret_cast<char*>(foo);
auto bar = reinterpret_cast<const MessageJ*>(temp);
MessageX*foo=newmessagex;
const auto temp=重新解释铸件(foo);
自动条=重新解释铸件(温度);
我的理解是,临时演员阵容使其合法化
编辑:
我得到了一些关于序列化、对齐、网络传递等方面的评论。这不是这个问题的重点
这是一个关于的问题。
严格别名是由C(或C++编译器)做出的一种假设,即对不同类型对象的指针的解引用永远不会引用相同的内存位置(即别名)
我想问的是:通过从
char*
强制转换,对const
对象的初始化是否会在该对象被强制转换到另一种类型的对象的下面进行优化,例如,我是从未初始化的数据强制转换的?使用(const
)char*
类型时没有别名问题,请参阅以下内容的最后一点:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- 对象的动态类型
- 对象动态类型的cv限定版本
- 与对象的动态类型类似的类型(如4.4中所定义)
- 与对象的动态类型相对应的有符号或无符号类型
- 一种类型,它是与对象的动态类型的cv限定版本相对应的有符号或无符号类型
- 一种聚合或联合类型,包括其元素或非静态数据成员(递归地包括子聚合或包含的联合的元素或非静态数据成员)中的上述类型之一
- 是对象动态类型的基类类型(可能是cv限定的)
- 字符或无符号字符类型
另一个答案很好地回答了问题(这是一个直接引用的第75页中的C++标准),所以我只指出你正在做的其他问题。 请注意,您的代码可能会遇到对齐问题。例如,如果MessageJ的对齐方式是4或8字节(通常在32位和64位机器上),严格来说,作为MessageJ指针访问任意字符数组指针是未定义的行为 您不会在x86/AMD64体系结构上遇到任何问题,因为它们允许未对齐的访问。然而,有一天你可能会发现你正在开发的代码被移植到了一个移动的ARM架构中,不对齐的访问将是一个问题 因此,你似乎在做一些不该做的事情。我将考虑使用序列化,而不是将字符数组作为MasaGeJ类型访问。唯一的问题不是潜在的对齐问题,另一个问题是数据在32位和64位体系结构上可能具有不同的表示形式 我的公司使用一个消息服务器,它将消息获取为const char*,然后将其强制转换为消息类型
auto bar = reinterpret_cast<const MessageJ*>(foo);
只要您的意思是它进行重新解释(或转换为重新解释)转换:
在本例中,由于严格的别名规则,编译器可以在foo
中假设设置*y
不会影响*x
的值。例如,它可以决定只返回-1作为常量。如果没有严格的别名规则,编译器将不得不假设更改*y
实际上可能会更改*x
的值。因此,它必须执行给定的操作顺序,并在设置*y
后重新加载*x
。在本例中,实施这种偏执似乎是合理的,但在不那么琐碎的代码中,这样做将极大地限制操作的重新排序和消除,并迫使编译器更频繁地重新加载值
以下是我以不同方式编译上述程序时在我的机器上的结果(适用于x86_64-Apple-darwin14.1.0的Apple LLVM v6.0):
在第一个示例中,foo
是一个const char*
,bar
是一个const MessageJ*
从foo
重新解释的。您进一步规定对象的底层类型实际上是MessageJ
,并且不通过const char*
进行读取。相反,它只被强制转换到const MessageJ*
,然后只从中执行读取。由于您不通过const char*
别名进行读写,因此通过第二个别名进行访问时不会出现别名优化问题。这是因为通过不相关类型的别名在基础内存上执行的操作没有潜在的冲突。但是,即使您确实通读了foo
,也可能没有潜在的问题,因为该类型允许这样的访问
MessageServer(char *foo)
{
if (somehow figure out that foo is actually a MessageJ*)
{
MessageJ *bar = reinterpret_cast<MessageJ*>(foo);
// operate on bar
}
}
// or
MessageServer()
{
char *foo = PopMessage();
if (somehow figure out that foo is actually a MessageJ*)
{
MessageJ *bar = reinterpret_cast<MessageJ*>(foo);
// operate on bar
}
}
#include <iostream>
int foo(int *x, long *y)
{
// foo can assume that x and y do not alias the same memory because they have unrelated types
// so it is free to reorder the operations on *x and *y as it sees fit
// and it need not worry that modifying one could affect the other
*x = -1;
*y = 0;
return *x;
}
int main()
{
long a;
int b = foo(reinterpret_cast<int*>(&a), &a); // violates strict aliasing rule
// the above call has UB because it both writes and reads a through an unrelated pointer type
// on return b might be either 0 or -1; a could similarly be arbitrary
// technically, the program could do anything because it's UB
std::cout << b << ' ' << a << std::endl;
return 0;
}
$ g++ -Wall test58.cc
$ ./a.out
0 0
$ g++ -Wall -O3 test58.cc
$ ./a.out
-1 0
#include <iostream>
char foo(char *x, long *y)
{
// can foo assume that x and y cannot alias the same memory?
*x = -1;
*y = 0;
return *x;
}
int main()
{
long a;
char b = foo(reinterpret_cast<char*>(&a), &a); // explicitly allowed!
// if this is defined behavior then what must the values of b and a be?
std::cout << (int) b << ' ' << a << std::endl;
return 0;
}
$ g++ -Wall test59.cc
$ ./a.out
0 0
$ g++ -O3 -Wall test59.cc
$ ./a.out
0 0
int value;
int *p = &value;
char *q = reinterpret_cast<char*>(&value);
const char *PopMessage()
{
static MessageJ foo = .....;
return reinterpret_cast<const char *>(&foo);
}
const char *ptr = PopMessage();
auto bar = reinterpret_cast<const MessageJ*>(foo);
auto baz = *bar; // OK, accessing a `MessageJ` via glvalue of type `MessageJ`
auto ch = ptr[4]; // OK, accessing a `MessageJ` via glvalue of type `char`
const char *PopMessage()
{
static char buf[200];
return buf;
}
enum MType { J,K };
struct MessageX { MType type; };
struct MessageJ {
MType type{ J };
int id{ 5 };
//some other members
};
const char* popMessage() {
return reinterpret_cast<char*>(new MessageJ());
}
void MessageServer(const char* foo) {
const MessageX* msgx = reinterpret_cast<const MessageX*>(foo);
switch (msgx->type) {
case J: {
const MessageJ* msgJ = reinterpret_cast<const MessageJ*>(foo);
std::cout << msgJ->id << std::endl;
}
}
}
int main() {
const char* foo = popMessage();
MessageServer(foo);
}