通过单一参考传递结构是否有任何隐藏成本?

时间:2012-11-10 06:16:14

标签: struct d

我最近正在阅读关于D中结构和类的this文章,并且作者一度评论

  

......这是结构的完美候选者。原因是它只包含一个成员,一个指向ALLEGRO_CONFIG的指针。这意味着我可以无需小心地传递值,因为它只是指针的大小。

这让我思考;真的是这样吗?我可以想到一些情况,其中相信你正在“免费”传递一个结构可能会有一些隐藏的陷阱。

请考虑以下代码:

struct S
{
    int* pointer;
}

void doStuff(S ptrStruct)
{
    // Some code here
}

int n = 123;
auto s = S(&n);
doStuff(s);

当s被传递给doStuff()时,是一个单指针(包裹在一个结构中)真的所有传递给该函数的是什么?在我的脑海中,似乎也会传递任何指向成员函数的指针,以及结构的类型信息。

这当然不会是类的问题,因为它们总是引用类型,但结构的值语义传递向我表明,如上所述的任何额外的“隐藏”数据都将传递给函数以及struct指向int的指针。这可能导致程序员认为他们正在绕过一个(假设一个64位机器)8字节指针,当他们实际传递一个8字节指针,加上其他几个8字节指向函数时,加上对象的typeinfo是多少字节。然后,粗心的程序员在堆栈上分配的数据远远超过预期。

我在这里追逐阴影,或者在传递带有单个引用的结构时这是一个有效的问题,并且认为你得到一个伪引用类型的结构? D中是否有某种机制可以防止这种情况发生?

2 个答案:

答案 0 :(得分:6)

我认为这个问题可以推广到包装原生类型。例如。你可以创建一个SafeInt类型,它包装并像int一样运行,但抛出任何整数溢出条件。

这里有两个问题:

  1. 编译器可能无法优化您的代码以及本机类型。

    例如,如果要包装int,则可能会实现重载算术运算符。一个足够聪明的编译器将内联这些方法,结果代码与int相同。在您的示例中,一个哑编译器可能正在以某种笨拙的方式编译解除引用(例如,获取结构的开始地址,添加指针字段的偏移量(为0),然后取消引用)。

    此外,当调用函数时,编译器可能决定以某种其他方式传递结构(由于例如差的优化或ABI限制)。这可能发生在例如如果编译器没有注意结构的大小,并以相同的方式处理所有结构。

  2. 如果你在函数中声明它,那么D中的
  3. struct类型确实可能有一个隐藏成员。

    例如,以下代码有效:

    import std.stdio;
    
    void main()
    {
        string str = "I am on the stack of main()";
    
        struct S
        {
            string toString() const { return str; }
        }
    
        S s;
        writeln(s);
    }
    

    它的工作原理是因为S保存了一个指向main()堆栈帧的隐藏指针。您可以通过在static前加static struct S前缀来强制结构没有任何隐藏指针。(

答案 1 :(得分:5)

没有传递任何隐藏数据。 struct完全由其中声明的内容(以及必要时的任何填充字节)组成,没有别的。无需传递类型信息和成员函数信息,因为它们都是静态。由于struct无法从另一个struct继承,因此没有多态性。