如何在D中创建可以与C代码接口的连续多维数组? 我对D语言感兴趣,并开始学习它,因为它认为它可以支持比C或C++更容易和更有效的编码,同时保留使用其他语言编写的代码的能力。

如何在D中创建可以与C代码接口的连续多维数组? 我对D语言感兴趣,并开始学习它,因为它认为它可以支持比C或C++更容易和更有效的编码,同时保留使用其他语言编写的代码的能力。,c,multidimensional-array,interface,d,C,Multidimensional Array,Interface,D,但是,在尝试构建2D阵列时,我发现: 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);

但是,在尝试构建2D阵列时,我发现:

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();
但对于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之后,我修复了我的bug,并做了更多的测试。我实际上需要一个动态数组,因为我从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();                     
auto-as=new-double[3][0];//w/C不一致:这是0行3列
自动广告=新双[][](0,3);//也可以是0 x 3
as~=[1,2,3];as~=[4,5,6];
ad~=[1,2,3];ad~=[4,5,6];//我可以附加到这两个
writeln(as);//->[[1, 2, 3], [4, 5, 6]]
书面形式(广告);//->[[1, 2, 3], [4, 5, 6]]
writefln(“作为容量:%dx%d”,作为.capacity,作为[0].capacity);
//->as容量:2x0
writefln(“ad容量:%dx%d”,ad.capacity,ad[0]。容量);
//->广告容量:3x3
双*p=&(如[0][0]);
对于(尺寸i=0;i 1 2 3 4 5 6
writeln();
p=&(ad[0][0]);
对于(尺寸i=0;i 1 2 3 4 5 6 0 0 0
writeln();
as
ad
实际上都是动态的,在追加时会调整大小,但它们的行为确实不同-请查看追加后的容量


所以现在的问题是,我可以依赖这些备选方案中任何一个的行为吗?语言是否至少保证了
新双精度[3][0]
语法?如果我知道不做什么,是否可以保证“动态”(
新双精度[][](0,3)
)数组的布局和连续性(例如,不直接更改
长度
属性)?

您使用的是动态数组。请注意,它们有一个可以设置的长度值,在您的GDC示例中显示为中断
nan
s的零。因为您将该长度值设置为零,所以您可以有效地告诉它忘记它应该承载1000个元素。另外,还允许D为将来的追加保留内存,因此sted动态阵列可能无法按预期方式对齐

如果要将多维数组发送到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;
}
请注意,它是由一千[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

好的,感谢@weltensturm,经过更多的测试和阅读,D的逻辑终于被“点击”,所以我总结一下

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

double[3][] b;                // allocated on the stack, or
auto b = new double[3][](0);  // allocated on the heap
都理解为的动态数组(长度为3的静态数组),并且不允许更改行的大小(但行的数量是):

这是对健壮性的最好保证。

因此,在使用声明为的数组时:

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];

被接受,它会生成一个包含静态列数和动态行数的数组。嗯…

这些零不是真正的零,而是未初始化的值-在示例中,我使用了
double
类型来查看初始化了哪些数。但是您是对的,是我对length属性的赋值改变了init虽然我觉得奇怪的是,
new double[3][1000]
中的索引顺序与C语法
double a[1000][3]相反,但我想当然地认为它不应该这样做
将是1000行。不过,还有一个问题-这个数组是在堆上还是在堆栈上分配的?正如我所说的,GDC示例中的零是长度值,实际上是零。长度从来都不是未初始化的。它存储在动态数组中的数据旁边。至于语法:if
double[3]
是三个双精度数组,
double[3][1000]
应该是一千个
double[3]数组
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];