使用联盟来避免动态分配问题

时间:2009-12-16 01:18:00

标签: c++ unions

我很好奇在访问返回可变长度结构的Win32 API时使用union是个好主意,以避免手动管理分配的内存。

请考虑以下事项:

void displayServices(std::wostream& log, std::tr1::shared_ptr<void> manager, LPENUM_SERVICE_STATUS currentServiceToDisplay, DWORD number, bool whitelist)
{
    static union //Don't care about being thread safe
    {
        QUERY_SERVICE_CONFIG svcConfig;
        unsigned char bufferSizer[8000];
    };
    for(DWORD idx = 0; idx < number; currentServiceToDisplay++, idx++)
    {
        DWORD garbage = 0;
        std::tr1::shared_ptr<void> currentServiceHandle(
            OpenService(reinterpret_cast<SC_HANDLE>(manager.get()), currentServiceToDisplay->lpServiceName, SERVICE_QUERY_CONFIG),
            serviceCloser);
        QueryServiceConfig(reinterpret_cast<SC_HANDLE>(currentServiceHandle.get()),
                            &svcConfig, 8000, &garbage);
        //Use svcConfig for something here.
    }
}

这有什么重大问题吗?

4 个答案:

答案 0 :(得分:4)

你的工会方法的一个问题是使用联合的整个想法是相当强制的,完全没有必要。您似乎想要做的就是用一些“大”大小的本地静态缓冲区替换动态内存分配。然后明确地做到这一点

unsigned char buffer[8000];    
QUERY_SERVICE_CONFIG *svcConfig = (QUERY_SERVICE_CONFIG *) buffer;
...
QueryServiceConfig(reinterpret_cast<SC_HANDLE>(currentServiceHandle.get()), 
  svcConfig, sizeof buffer, &garbage);
// Use `svcConfig` here, keeping in mind it is a pointer now

我试图通过特别使用工会来实现目标并不清楚。你认为联合的方法在某种程度上更优雅吗?我不这么认为。

更新:在其中一条评论中已经注意到,这种方法可能不适用于执行严格别名优化的编译器。由于多个原因,评论绝对不正确。首先,上述方法根本不依赖于任何依赖于别名的行为,因为所有访问都应该仅通过svcConfig指针和svcConfig指针来执行。其次,如果别名数据是字符数组,则永远不会执行严格的别名优化。语言规范明确允许通过字符数组进行别名访问,这就是所有优化编译器在涉及字符数组时禁用其严格别名优化的原因。任何未能这样做的编译器都会被破坏,因此不值得考虑。

答案 1 :(得分:3)

我不知道有任何重大问题。但是,我认为只是声明缓冲区,然后使用指针来获取所需的结构,而不是使用联合,我会更清楚。

我想我从你的例子中得不到的是这样做的重点是什么。

编辑:我的意思是,这样做:

void displayServices(std::wostream& log, std::tr1::shared_ptr<void> manager, LPENUM_SERVICE_STATUS currentServiceToDisplay, DWORD number, bool whitelist)
{
    unsigned char bufferSizer[8000];
    QUERY_SERVICE_CONFIG *pSvcConfig = reinterpret_cast<QUERY_SERVICE_CONFIG*>(bufferSizer);

    for(DWORD idx = 0; idx < number; currentServiceToDisplay++, idx++)
    {
        DWORD garbage = 0;
        std::tr1::shared_ptr<void> currentServiceHandle(
            OpenService(reinterpret_cast<SC_HANDLE>(manager.get()), currentServiceToDisplay->lpServiceName, SERVICE_QUERY_CONFIG),
            serviceCloser);
        QueryServiceConfig(reinterpret_cast<SC_HANDLE>(currentServiceHandle.get()),
                            pSvcConfig, sizeof(bufferSizer), &garbage);
        //Use pSvcConfig for something here.
    }
}
至少对我来说,这是更清楚的。

答案 2 :(得分:1)

除了使用比必要更多的堆栈空间之外,没有。

更新

是的,完全错过了static关键字,抱歉。

只要你不需要从多个线程中使用它就可以正常工作,并且函数不可能重入。

答案 3 :(得分:0)

另一种方法,只使用基类型的静态数组:

void displayServices(std::wostream& log, std::tr1::shared_ptr<void> manager, LPENUM_SERVICE_STATUS currentServiceToDisplay, DWORD number, bool whitelist)
{
    static QUERY_SERVICE_CONFIG svcConfig[8000/sizeof(QUERY_SERVICE_CONFIG)]; // Or just pick an arbitrary number

    for(DWORD idx = 0; idx < number; currentServiceToDisplay++, idx++)
    {
        DWORD garbage = 0;
        std::tr1::shared_ptr<void> currentServiceHandle(
                OpenService(reinterpret_cast<SC_HANDLE>(manager.get()), currentServiceToDisplay->lpServiceName, SERVICE_QUERY_CONFIG),
                serviceCloser);
        QueryServiceConfig(reinterpret_cast<SC_HANDLE>(currentServiceHandle.get()),
                                                svcConfig, sizeof(svcConfig), &garbage);
        //Use svcConfig for something here.
    }
}