演员究竟做了什么?

时间:2015-07-24 11:53:58

标签: casting type-conversion union d

我正在玩整数数组散列以及从表示到另一个的不同方式。我最终得到了以下内容:

void main(string[] args) {
    import std.algorithm, std.array, std.conv, std.stdio, std.digest.md;

    union hashU {
        ubyte[] hashA;
        int[]   hashN;
    };

    hashU a;
    auto md5 = new MD5Digest();

    a.hashN = [1, 2, 3, 4, 5];

    /* Using an union, no actual data conversion */
    md5.put( a.hashA );
    auto hash = md5.finish();
    writeln(hash);
    // [253, 255, 63, 4, 193, 99, 182, 232, 28, 231, 57, 107, 18, 254, 75, 175]

    /* Using a cast... Doesn't match any of the other representations */
    md5.put( cast(ubyte[])(a.hashN) );
    hash = md5.finish();
    writeln(hash);
    // [254, 5, 74, 210, 231, 185, 139, 238, 103, 63, 159, 242, 45, 80, 240, 12]

    /* Using .to! to convert from array to array */
    md5.put( a.hashN.to!(ubyte[]) );
    hash = md5.finish();
    writeln(hash);
    // [124, 253, 208, 120, 137, 179, 41, 93, 106, 85, 9, 20, 171, 53, 224, 104]

    /* This matches the previous transformation */
    md5.put( a.hashN.map!(x => x.to!ubyte).array );
    hash = md5.finish();
    writeln(hash);
    // [124, 253, 208, 120, 137, 179, 41, 93, 106, 85, 9, 20, 171, 53, 224, 104]
}

我的问题如下:演员是做什么的?我原以为它要像.to一样做!或联盟技巧,但似乎并非如此。

3 个答案:

答案 0 :(得分:4)

我认为Colin Grogan说得对,但他的措辞有点令人困惑。

使用union,只需重新解释数组,根本不会进行任何计算/计算。重新解释int[]的指针和长度以引用ubyte元素。之前:5个整数,之后:5个字节。

演员比这更聪明:它调整数组的长度,使其引用与以前相同的内存。之前:5个整数,之后:20个字节(5 * int.sizeof / ubyte.sizeof = 5 * 4/1 = 20)。

union和cast都将int的字节重新解释为ubytes。也就是说,int值1将产生4 ubytes:0,0,0,1或1,0,0,0,具体取决于endianess。

to变体将每个元素转换为新元素类型。之前:5个整数,之后:5个ubytes,其值与整数相同。如果其中一个int不能转换为ubyte,to将抛出异常。

在各种转换后打印元素可能有助于澄清在哪里发生的事情:

void main()
{
    import std.algorithm, std.array, std.conv, std.stdio;

    union hashU
    {
        ubyte[] hashA;
        int[]   hashN;
    }
    hashU a;
    a.hashN = [1, 2, 3, 4, 5];

    writeln( a.hashA ); /* union -> [1, 0, 0, 0, 2] (depends on endianess) */

    writeln( cast(ubyte[])(a.hashN) );
        /* cast -> [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0]
        (depends on endianess) */

    writeln( a.hashN.to!(ubyte[]) ); /* `to` -> [1, 2, 3, 4, 5] */
}

答案 1 :(得分:2)

据我所知,cast只是告诉编译器开始与你投射的内存块进行对话。

示例中的最后两个选项实际上将数字转换为新类型,因此执行一些实际工作。这就是他们最终成为相同价值观的原因。

前两个例子中的问题是int []的内存大于ubyte []。 (每个元素4个字节,每个元素1个字节)

我编辑了前两种方法:

    /* Using an union, no actual data conversion */
md5.put( a.hashA );
auto hash = md5.finish();
writefln("Hash of: %s -> %s", a.hashA, hash);
// Hash of: [1, 0, 0, 0, 2] -> [253, 255, 63, 4, 193, 99, 182, 232, 28, 231, 57, 107, 18, 254, 75, 175] 
// notice 5 bytes in first array

/* Using a cast... Doesn't match any of the other representations */
md5.put( cast(ubyte[])(a.hashN) );
hash = md5.finish();
writefln("Hash of: %s -> %s", cast(ubyte[])(a.hashN), hash);
// Hash of: [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0] -> [254, 5, 74, 210, 231, 185, 139, 238, 103, 63, 159, 242, 45, 80, 240, 12] 
// notice 20 bytes (4 x 5 bytes) in first array

所以在第一个中,你正在读取ubyte []的字节长度。在第二个你将int []的长度转换为ubyte []。

编辑:问题尚不清楚。联合非常愚蠢,它只是将所有值存储在同一个内存中。当你去读取其中任何一个时,它只会读取该内存的X位,具体取决于你正在阅读的类型的长度。

因为你正在读取int [] THEN,所以它会读取所有20个字节并将它们转换为ubyte []。这当然不同于只读取ubyte []变量的5个字节。

我想我在那里有道理:))

答案 2 :(得分:0)

cast是开发人员需要执行显式类型转换时使用的D运算符。