如何在D中创建连续的多维数组,可以与C代码接口?

时间:2016-04-27 20:45:40

标签: c multidimensional-array interface d

我对D语言感兴趣并开始学习它,因为它可以支持比C或C ++更简单,更有效的编码,同时保留使用这些其他语言编写的代码的能力。

然而,尝试构建一个二维数组,我观察到了这一点:

auto a = new double[][](1000,3);    // clean and seemingly elegant
a.length = 0;                       // To fill the array by ~= operator
a ~= [1, 2, 3];                     // fill the first row
writeln(a);                         // -> [[1, 2, 3]]  (OK)
writeln(a.capacity,a[0].capacity);  // -> 1 3  (should be 1000 3)

它已经感觉不对,因为我明确表示希望为1000x3数字保留内存。尽管如此,Digital Mars D和GCC gdc编译器都会产生相同的结果。

所以为了使数组真的是二维的,我添加了另一行。

a ~= [3, 4, 5];                     // add another row
writeln(a);                         // -> [[1, 2, 3], [4, 5, 6]] (OK)
writeln(a.capacity,a[0].capacity);  // -> 3 3

如果它意味着a的内容占用了大小为3*3*double.sizeof和行主要布局的固体内存块,那么这是可以接受的。但是更多的惊喜正在发生 - 当我想从C的角度检查这个数组的布局时:

double* p = &(a[0][0]);             // get a C-like pointer to the data
for(size_t i=0; i<20; i++) {
    writef("%.0f ", *(p+i));        // should be: 1 2 3 4 5 6 nan nan ...
    }
writeln();

我得到了令人费解的结果,并且编译器之间有所不同。 Dmd与libphobos2(版本2.071.0)说:

1 2 3 0 0 0 0 0 4 5 6 0 0 0 0 0 0 0 0 0

但是使用gdc(GCC版本4.8.4),相同的代码打印出来:

1 2 3 0 nan nan nan 0 nan nan nan 0 nan nan nan 0 nan nan nan 0

糟糕。这既不可用也不便携。这个数组根本不是连续的(所以我读了一些其他内存),或者两个实现都有错误,因为应该保证nan的初始化。好吧,我已经知道我可以使用malloc并像在C中那样做所有事情,但是我没有看到学习另一个巨大的野兽的优势,这种语言有着自己古怪的黑客。

是否有一种干净的方法来制作具有C布局的连续多维数组,而不会失去D的优势,如内存管理,范围,切片,算法等?

修改

在@weltensturm回答之后我修复了我的错误并且我做了一些测试。我实际上需要一个动态数组,因为我从FIFO中读取数字并且我不知道先前有多少行。这段代码显然有效:

auto as = new double[3][0];     // Inconsistent w/C: this is 0 rows by 3 columns
auto ad = new double[][](0,3);  // also 0 x 3
as ~= [1,2,3];  as ~= [4,5,6];
ad ~= [1,2,3];  ad ~= [4,5,6];  // I can append to both
writeln(as);                    // -> [[1, 2, 3], [4, 5, 6]]
writeln(ad);                    // -> [[1, 2, 3], [4, 5, 6]]
writefln("as capacity: %dx%d", as.capacity, as[0].capacity);
                                // -> as capacity: 2x0
writefln("ad capacity: %dx%d", ad.capacity, ad[0].capacity);
                                // -> ad capacity: 3x3
double* p = &(as[0][0]);
for(size_t i=0; i<6; i++) writef("%.0f ", *(p+i));  // -> 1 2 3 4 5 6
writeln();
p = &(ad[0][0]);
for(size_t i=0; i<9; i++) writef("%.0f ", *(p+i));  // -> 1 2 3 4 5 6 0 0 0
writeln();                     

asad实际上都是动态的,并且在附加时调整大小,但它们的行为却不一样 - 查看附加后的容量。

现在问题是,我可以依赖任何这些替代方案的行为吗?至少对于new double[3][0]语法,它是否由语言保证?是否有可能保证&#34;动态&#34;的布局和连续性。 (new double[][](0,3))数组,前提是我知道要做什么 not (例如不直接更改length属性)?

2 个答案:

答案 0 :(得分:3)

您正在使用动态数组。请注意,它们具有可以设置的长度值,在GDC示例中显示为中断nan的零。因为您将该长度值设置为零,所以有效地告诉它忘记它应该携带1000个元素。此外,D允许为将来的附加保留内存,因此嵌套的动态数组可能与您期望的不一致。

如果要将多维数组发送到C,最好使用静态数组或连续动态数组。您的静态数组示例:

void main()
{
    auto a = new double[3][1000];
    a[0] = [1, 2, 3];
    a[1] = [3, 4, 5];
    double* p = &(a[0][0]);
    for(size_t i=0; i<20; i++) {
        writef("%.0f ", *(p+i));
    }
    writeln;
}

打印

1 2 3 3 4 5 nan nan nan nan nan nan nan nan nan nan nan nan nan nan

请注意,它是一个包含一千个[3]数组的数组,因此[0]和[1]相邻。

编辑以接听您的修改:

您有两个选项,两者看起来都与C相同。

// dynamic array of double[3]
double[3][] a;

// or one-dimensional array
double[] a;

a.reserve(1000); // ensure space for 1000 new elements (use 3000 for the second option)

a ~= [3, 5, 7]; // works for both
// pass to C using a.ptr

答案 1 :(得分:1)

好的,感谢@weltensturm以及更多的测试和阅读D的逻辑&#34;点击&#34;,所以我总结一下。

基本上,要依赖内存中的多维动态D数组布局,除了最外层的所有维度都必须是静态的,即在编译时已知。这些声明:

double[3][] b;                // allocated on the stack, or
auto b = new double[3][](0);  // allocated on the heap

都被理解为(长度为3的静态数组)的动态数组,并且不允许更改行的大小(但是行数是):

b ~= [1,2,3];                 // works
b ~= [4,5,6,7.];              // Compilation error!

这是人们可以获得稳健性的最佳保证。

因此在使用声明为:

的数组时
double a[];                   // or
double[][] a;                 //, or
auto a = new double[][](0,3); // these will be initial dimensions, not static!

是可能的,它要求麻烦,因为允许D运行时在任何更改数组内容的操作中更改任何维度。例如,使用上述任何声明,可以追加不均匀的行:

a ~= [1,2,3];
a ~= [4,5,6,7.];              // accepted

如果它触发了内存重新分配,它也更有可能改变内存中数据的布局。

作为旁注,有趣的是编译器错误:

auto b = new double[3][];
Error: new can only create structs, dynamic arrays or class objects, not double[3][]'s

因为它不是动态数组,而是:

auto b = new double[3][0];

被接受,它创建一个具有静态列数和动态行数的数组。嗯......