拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 跨DLL边界的严格别名

跨DLL边界的严格别名

白鹭 - 2022-03-04 1951 0 0

我一直在查看 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 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *