我一直在查看 C 严格的别名规则,这让我想到了我之前作业中的一些代码。我相信说代码违反了严格的别名规则,但很好奇为什么我们没有遇到任何问题或编译器警告。我们使用核心 .DLL 来接收传递给服务器应用程序的网络讯息。一个(非常)简化的例子:
#include <iostream>
#include <cstring>
using namespace std;
// These enums/structs lived in a shared .h file consumed by the DLL and server application
enum NetworkMessageId : int
{
NETWORK_MESSAGE_LOGIN
// ...
};
struct NetworkMessageBase
{
NetworkMessageId type;
size_t size;
};
struct LoginNetworkMessage : NetworkMessageBase
{
static constexpr size_t MaxUsernameLength = 25;
static constexpr size_t MaxPasswordLength = 50;
char username[MaxUsernameLength];
char password[MaxUsernameLength];
};
// This buffer and function was created/exported by the DLL
char* receiveBuffer = new char[sizeof(LoginNetworkMessage)];
NetworkMessageBase* receiveNetworkMessage()
{
// Simulate receiving data from network, actual production code provided additional safety checks
LoginNetworkMessage msg;
msg.type = NETWORK_MESSAGE_LOGIN;
msg.size = sizeof(msg);
strcpy(msg.username, "username1");
strcpy(msg.password, "qwerty");
memcpy(receiveBuffer, &msg, sizeof(msg));
return (NetworkMessageBase*)&receiveBuffer[0]; // I believe this line invokes undefined behavior (strict aliasing)
}
// Pretend main is the server application
int main()
{
NetworkMessageBase* msg = receiveNetworkMessage();
switch (msg->type)
{
case NETWORK_MESSAGE_LOGIN:
{
LoginNetworkMessage* loginMsg = (LoginNetworkMessage*)msg;
cout << "Username: " << loginMsg->username << " Password: " << loginMsg->password << endl;
}
break;
}
delete [] receiveBuffer; // A cleanup function defined in the DLL actually did this
return 0;
}
据我了解,receiveNetworkMessage()
呼叫未定义的行为。我读过严格的别名 UB 通常与编译器优化/假设有关。我认为这些优化与这种情况无关,因为 .DLL 和服务器应用程序是分开编译的。那是对的吗?
最后,客户端应用程序还共享了提供的示例 .h,它用于创建一个LoginNetworkMessage
逐字节流式传输到服务器的示例。这是便携的吗?撇开包装/字节顺序问题不谈,我相信这不是因为LoginNetworkMessage
' 的布局是非标准的,所以成员排序可能会有所不同。
uj5u.com热心网友回复:
正确的。
不,依赖二进制兼容性的网络通信不可移植。
即使对于非标准布局类,成员的顺序也得到保证。问题(除了字节序)是用于对齐目的的填充的数量和位置、基本型别的大小、字节中的位数(尽管公平地说,非 8 位字节的网络连接硬件可能不是你需要支持的东西)。
uj5u.com热心网友回复:
是的。这使得代码在实践中是安全的(即使标准不知道任何 DLL 并且无论如何都认为它未定义)。
对于不同的翻译单元也是如此,除非启用了整体程序优化。
正如另一个答案所说,这里唯一的潜在问题是结构布局的可移植性。
0 评论