Matrix转换从Java移植到C,不兼容的类型问题

我必须在C中移植一些Java方法,有一个Java背景但我在C编程中是一个总的菜鸟

在java中

float[][] traspose(float Xy[][]) { float result[][]=new float[5000][3000]; for(int i = 0; i < m; i++) { for(int j = 0; j < n; j++) { result[i][j] = Xy[j][i]; } } return result; } 

我的C移植尝试

 float traspose(int m, int n, float Xy[m][n]) { int i,j; float result[5000][3000]; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { result[i][j] = Xy[j][i]; } } return result; } 

这不起作用,并获得不兼容的类型错误。

我的2个问题

1)我应该如何修复我的代码? 谷歌搜索我已经看到一些关于在C中返回矩阵的问题,但不是很清楚,并且在大多数情况下建议使用一种不暗示使用返回的方法。

2)我已经看到,C中的这种操作通常是在没有返回类型方法的情况下编写的,例如对常量进行操作的void方法或者直接在main中编写代码。 为什么?

编辑

按照我的建议,我试图编码这个

 float **transpose(int m, int n, float Xy[]) { int i,j; float **result = allocate_mem_m(m,n); for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { result[i][j] = Xy[j*n+i]; } } return result; } int main(int argc, char **argv) { printf("Hello World!"); float matrix[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; printf("Matrix created\n"); int size=3; print(size, size, matrix); float **transposedmat = transpose(size, size, &matrix[0][0]); printMat(size, size, transposedmat); return 0; } 

但不幸的是,当我调用trasposition方法时程序崩溃了。

PS我想用标准C编译代码(不是在C99中)

严格来说,使用指向浮点指针的答案在技术上并不正确,因为浮点数的二维数组与指向浮点数的指针不同。

这将是等效的C代码:

 #include  float (*transpose(int m, int n, float Xy[m][n]))[5000][3000] { float (*result)[5000][3000] = malloc(sizeof(*result)); for(int i = 0; i < m; i++) { for(int j = 0; j < n; j++) { (*result)[i][j] = Xy[j][i]; } } return result; } 

这适用于指针:函数内的第一行为5000 x 3000的2D数组分配空间,在Java代码中生成副本,并返回指向新数组的指针。 请注意,完成后必须释放内存(通过调用free() )。

该函数返回一个指向数组的指针,这意味着你必须像这样使用它:

 float (*array)[5000][3000] = transpose(m, n, Xy); 

然后你可以使用(*array)[i][j]从新数组访问元素i,j

要免费,请执行以下操作:

 free(array); 

最后,记得用C99支持编译它 - 你需要它,因为参数列表中的变量长度数组Xy 。 如果您正在使用gcc,可以使用-std=c99来实现

在其他答案中提出的解决方案可能足以满足您的目的,但请记住,使用float **作为2D数组有一些注意事项和“陷阱”。 例如,使用float **解决方案,您必须在释放array本身之前手动释放array[i]每个位置; sizeof不会告诉您“模拟”2D数组的真实大小,并且浮点数不会连续存储在内存中。

2)我已经看到,C中的这种操作通常是在没有返回类型方法的情况下编写的,例如对常量进行操作的void方法或者直接在main中编写代码。 为什么?

有关直接在main()编写的代码的部分并不常见。 也许您刚看到一些教程示例。 在更大的程序中,一般来说,这当然不在main()

如果您不想分配内存,那么在没有返回类型的情况下编写它可能很有用:您可以将其留给调用者。 这很重要,因为您不会释放释放在调用者上分配的内存的负担。 相反,您会收到一个指向调度程序为您提供的已分配内存缓冲区的指针,并将结果写入其中。 在内存管理方面,这通常是一种优越的方法,但当然,许多设计选择和细节很少可以迅速改变这一点。

更新 (如何在没有C99支持的情况下编译它):

好吧,这里的问题是Xy可能是一个任意长度的2D数组,也就是说,你想用任何 2D数组调用transpose() (这就是为什么你给它mn为维度)。

C没有将任意大小的2D数组传递给函数的直接方法。 在C99中添加了这种支持。 如果你想在C89中这样做,一个已知的解决方法是使用数组在连续的内存位置线性布局的事实,因此使用它就好像它是一个m*n浮点数的一维数组。 换句话说,您可以滚动自己的索引。 由于C数组以行主顺序存储,因此Xy[i][j]Xy_flat[i*n+j] 。 因此,实际上, transpose()接收指向Xy第一个元素的指针,并将Xy视为一维数组。 我们只需用Xy[i*n+j]替换Xy[i][j] Xy[i*n+j]

 /* C89 version */ #include  float (*transpose2(int m, int n, float Xy[]))[5000][3000] { float (*result)[5000][3000] = malloc(sizeof(*result)); int i, j; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { (*result)[i][j] = Xy[j*n+i]; } } return result; } 

对于来自Java的人来说,这可能看起来很奇怪而且微不足道,但C通常在较低级别工作。

要使用此函数,必须为其指定Xy的第一个元素。 这是一个例子:

 float matrix[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; float (*transposed)[5000][3000] = transpose(3, 3, &matrix[0][0]); /* Use (*transposed)[i][j]... */ free(transposed); 

如果您不想因为固定的硬编码维度而在整个代码中使用(*array)[5000][3000] ,那么您当然可以在其他答案中使用这些解决方案,但始终要记住它们之间的差异指向float指针的指针和float的2D数组。 由于您似乎更喜欢这种方法,因此以下是代码的外观:

 float **allocate_mem_m(int m, int n) { int i; float **arr = malloc(n*sizeof(*arr)); for(i=0;i 

我冒昧地改变你的allocate_mem_m()只接收维度,并返回带有分配内存的指针。 我认为当你使用float ***时,它开始变得有点过于复杂。 这不是必要的。

作为建议,我同样会添加一个free_mem_m()来简化释放已分配内存的过程:

 void free_mem_m(int m, float **array) { int i; for (i = 0; i < m; i++) { free(array[i]); } free(array); } 

这是完整的代码清单:

 #include  float **allocate_mem_m(int m, int n) { int i; float **arr = malloc(n*sizeof(*arr)); for(i=0;i 

并举例说明:

 int main(void) { float Xy[3][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 } }; float **transposed = transpose(3, 3, &Xy[0][0]); int i, j; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) printf("%f ", transposed[i][j]); printf("\n"); free_mem_m(3, transposed); return 0; } 

请注意, transpose()采用方形矩阵(因为您为m浮点数分配了n位置的块)。 如果要将其与矩形矩阵一起使用,则必须分配m个块来代替n浮点数。

现场演示工作: http : //ideone.com/CyNdpn

最后要注意的是 :将二维arrays视为一维arrays的黑客行为既旧又棘手; 它通常被认为是不好的做法(在我看来),你应该避免这种聪明的代码。 但是如果你不想使用C99function,这就是你剩下的。

对于感兴趣的读者,冒着成为垃圾邮件发送者的风险,我在我的博客中写了两篇关于将这个想法概括为具有N维的任意数组的文章。 它更深入地介绍了为什么需要这样做,以及如何做到这一点: http : //codinghighway.com/? p = 1159和http://codinghighway.com/?p=1206

读取C中的指针。数组名称result&result[0]result数组的第一个元素的地址)相同,因此当您返回result ,您将返回指向该数组的指针。

但是,您在traspose函数的堆栈上声明result 。 函数返回时,将破坏此局部变量。 即使result作为指针返回,它也不是有效的指针,因为一旦函数返回,内存就会消失。

尝试使用mallocresult数组放在堆(动态内存)上,然后返回指向该数组的指针。 您还需要将函数的返回类型更改为指针。

编辑例如:创建指向浮点指针的指针。 对于该数组的每个元素,malloc为每个浮点数的空间。

 float **array; array = malloc(rows * sizeof(float *)); for (i = 0; i < rows; i++) { array[i] = malloc(cols * sizeof(float)); } 

用原始数组中的任何内容填充数组。

 for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { array[i][j] = 0; //VALUE } } 

最简单的方法是将目标数组(保存转置版本的数组)作为参数传递给函数。 这使得内存管理的责任留给调用者(它应该在哪里),给你一些类型安全,并且更清洁:

 #include  // Re-written method void transpose(int m, int n, float srcXy[m][n], float dstYx[n][m]) { int i,j; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { dstYx[j][i] = srcXy[i][j]; } } } // (just for demo purposes) void print_matrix(int m, int n, float matrix[m][n]) { int i,j; for (i=0; i