我有一个练习,我应该使用固定大小的数组和输入/输出参数在矩阵上做东西(添加,扫描,打印等),但我想在任意长度上做矩阵并返回它们而不是每次添加(in /) out 参数(因此可能允许更“功能”的样式)。
由于我想返回它们,我想我可能需要malloc来保持数组在内存中传递函数作用域。由于我想使用多维下标符号(mat[x][y]
而不是mat[x*len+y]
或mat+x*len+y
),我想我应该使用某种vla或者转换......但似乎强制转换为数组(但是我会经常返回指针,如果我不能投射,如何对它们使用下标符号?),我明显地
“可能不会像编译器那样初始化一个可变大小的对象”(即使它不是直接的数组而是指向数组的指针),就像使用这种表示法一样:
int *tab[x][y]=malloc(x*y*sizeof(int));
如果我手动用3
等常量值替换x和y,我也会得到“无效的初始值设定项”。
我通过了将近一周的搜索,也许这是不可能的,我应该继续前进...我也发现了这个符号,对我来说看起来像函数指针表示法,除非它是一种优先考虑*
运算符的方法...
int (*tab)[x][y]=malloc(x*y*sizeof(int));
但是我并不完全清楚这种符号,因为这样就可以从打印/填充数组中获取随机值。
以前我曾尝试使用VLA(可变长度数组)和GNU扩展来提供数组长度作为参数:
void
printMat (int h, int w; int tab[h][w], int h, int w)
{
[code using tab[x][y]]
}
但我很快就意识到我需要用指针和malloc处理一个“add”函数,添加两个矩阵并返回一个指向新malloc'ed矩阵的指针......
我特别想知道,如果我不够具体,我应该如何声明参数和返回类型,以便能够将它们用作多维数组而不必使用中间变量,而实际传递一个指针(无论如何,这已经是正常的多维数组作为参数做对了吗?)
好的经过多次测试和尝试后,它现在按照我的意图工作,即使我不确定是否已经很好地理解了所有内容,尤其是指针是什么,什么不是(我可能会因为试图弄清楚而感到困惑) gdb这个,我应该进一步研究一下普通的单维或多维数组是否被gdb视为地址,等等),而且今天我的睡眠/休息时间并没有达到最佳状态。 / p>
现在,我想对我最初问题的第二部分给出正确答案:如何回归?是否存在一个适当的泛型类型(除了无意义的void*
),它可能被用于指向二维数组的指针(如int(*)[][]
但这样可行吗?)?如果太通用,什么是投射返回指针的正确方法,所以我可以使用多维下标表示法? (int(*)[3][3])
是否正确?
但是,如果我没有得到任何令人满意的结果(一个合理的 - 足够的“在C中它是不可能的”我猜),我会将@JohnBod当前的答案设置为解决问题,因为他给出了多维vla malloc的确认通过对多维数组的完整和解释性答案,完全回答问题的第一部分,并在第二部分的路径上给出了几个答案(如果有的话)。
#include <stdio.h>
#include <stdlib.h>
void
print_mat (int x, int y; int mat[x][y], int x, int y)
{
for (int i = 0; i < x; i++)
{
for (int j=0; j < y ; j++)
printf("%d ", mat[i][j]);
putchar('\n');
}
putchar('\n');
}
void*
scan_mat (int x, int y)
{
int (*mat)[x][y]=malloc(sizeof(*mat));
for (int i = 0; i < x ; i++)
for (int j = 0; j < y; j++)
{
printf("[%d][%d] = ", i, j);
scanf("%d", &((*mat)[i][j]));
}
return mat;
}
void*
add_mat (int x, int y; int mat1[x][y], int mat2[x][y], int x, int y)
{
int (*mat)[x][y]=malloc(*mat);
#pragma GCC ivdep
for (int i = 0; i < x ; i++)
for (int j = 0; j < y; j++)
(*mat)[i][j]=mat1[i][j]+mat2[i][j];
return mat;
}
int
main ()
{
int mat1[3][3] = {1, 2, 3,
4, 5, 6,
7, 8, 9},
(*mat2)[3][3] = scan_mat(3, 3);
print_mat(mat1, 3, 3);
print_mat(*mat2, 3, 3);
print_mat((int(*)[3][3])add_mat(mat1, *mat2, 3, 3), 3, 3); // both appears to work… array decay?
print_mat(*(int(*)[3][3])add_mat(mat1, *mat2, 3, 3), 3, 3);
printf("%d\n", (*(int(*)[3][3])add_mat(mat1, *mat2, 3, 3))[2][2]);
return 0;
}
和输入/输出:
[0][0] = 1
[0][1] = 1
[0][2] = 1
[1][0] = 1
[1][1] = 1
[1][2] = 1
[2][0] = 1
[2][1] = 1
[2][2] = 1
1 2 3
4 5 6
7 8 9
1 1 1
1 1 1
1 1 1
2 3 4
5 6 7
8 9 10
2 3 4
5 6 7
8 9 10
10
答案 0 :(得分:6)
如果要分配类型为T
的缓冲区,则典型过程为
T *ptr = malloc( sizeof *ptr * N ); // sizeof *ptr == sizeof (T)
您为N
类型的T
元素分配了足够的空间。
现在让我们用数组类型T
替换R [M]
:
R (*ptr)[M] = malloc( sizeof *ptr * N ); // sizeof *ptr == sizeof (R [M])
您为N
类型的R [M]
元素分配了足够的空间 - IOW,您刚刚为N
M
数组R
分配了足够的空间1}}。请注意,语义与上面T
的数组完全相同;所有改变的都是ptr
的类型。
将其应用于您的示例:
int (*tab)[y] = malloc( sizeof *tab * x );
然后您可以像对待任何2D数组一样索引tab
:
tab[x][y] = new_value();
修改强>
回答评论:
然而,我仍然不明白:“(* tab)”语法的含义是什么?它不是我猜的函数指针,但是为什么不用* tab而不用括号工作:实际的不同含义是什么?为什么它不起作用,然后改变了什么呢?
下标[]
和函数调用()
运算符的优先级高于一元*
,因此声明如
int *a[N];
被解析为
int *(a[N]);
并将a
声明为指针数组至int
。要声明指向数组的指针,必须使用标识符对*
运算符进行显式分组,如下所示:
int (*a)[N];
这将a
声明为指向int
的数组的指针。同样的规则适用于函数声明。这是一个方便的总结:
T *a[N]; // a is an N-element array of pointers to T
T (*a)[N]; // a is a pointer to an N-element array of T
T *f(); // f is a function returning pointer to T
T (*f)(); // f is a pointer to a function returning T
在您的代码中,
int *tab[x][y]=malloc(x*y*sizeof(int));
将tab
声明为指针的2D数组,而不是指向2D数组的指针,并且对malloc(...)
的调用不是2D数组的有效初始值设定项宾语。
语法
int (*tab)[x][y]=malloc(x*y*sizeof(int));
将tab
声明为指向2D数组的指针,对malloc
的调用是一个有效的初始值设定项。
但是...
使用此声明,您必须在索引之前明确取消引用tab
,如下所示:
(*tab)[i][j] = some_value();
您没有编入tab
的索引,而是在索引tab
指向的内容。
请记住,在C中,声明模仿使用 - 声明中声明符的结构与可执行代码中的结果相匹配。如果您有指向int
的指针并且想要访问指向的值,则使用一元*
运算符:
x = *ptr;
表达式的类型 *ptr
为int
,因此ptr
的声明已写入
int *ptr;
对于数组,如果数组的i
元素的类型为int
,那么表达式 arr[i]
的类型为int
因此arr
的声明写成
int arr[N];
因此,如果您将tab
声明为
int (*tab)[x][y] = ...;
然后要编入索引,你必须写
(*tab)[i][j] = ...;
我展示的方法避免了这种情况。请记住,数组下标操作a[i]
已定义为*(a + i)
- 给定地址a
,偏移i
元素(不是字节! )来自a
并取消引用结果。因此,以下关系成立:
*a == *(a + 0) == a[0]
这就是为什么你可以在指针表达式和数组表达式上使用[]
运算符。如果您将缓冲区分配为
T *p = malloc( sizeof *p * N );
您可以p[i]
访问每个元素。
所以,给出像
这样的声明T (*a)[M];
我们有关系
(*a)[i] == (*(a + 0))[i] == (a[0])[i] == a[0][i];
因此,如果我们将数组分配为
T (*a)[M] = malloc( sizeof *a * N );
然后我们可以将a
的每个元素编入索引为
a[i][j] = some_value();