Return 一个 malloc 矩阵,同时能够使用下标符号
Return a malloc’ed matrix while being able to use subscript notation
我有一个练习,我应该使用固定大小的数组和 in/out 参数来对矩阵进行操作(add、scanf、print 等),但我想这样做在任意长度的矩阵和 return 它们上,而不是每次添加更多的 (in/)out 参数(因此可能允许更“功能”的风格)。
因为我想 return 它们,我想我可能需要 malloc 来保持内存中的数组通过函数作用域。因为我想使用多维下标符号(mat[x][y]
而不是 mat[x*len+y]
或 mat+x*len+y
)我想我应该使用某种 vla 或转换......但它似乎禁止转换为数组(但是我会经常 return 指针,如果我不能转换,如何在它们上使用下标符号?),我明显
正如编译器所说,“可能不会初始化可变大小的对象”(即使它不是直接的数组而是指向数组的指针),比如使用这种表示法:
int *tab[x][y]=malloc(x*y*sizeof(int));
如果我手动将 x 和 y 替换为 3
等常量值,我也会得到“无效的初始值设定项”。
我搜索了将近一个星期,也许那是不可能的,我应该继续前进……我还发现了这个符号,对我来说它看起来像函数指针符号,除非它是一种优先 *
运算符…
int (*tab)[x][y]=malloc(x*y*sizeof(int));
但是我不确定是否完全理解这个符号,因为然后用这种方式从 printed/filled 数组中获取随机值。
之前我曾尝试使用 VLA(可变长度数组)和 GNU 扩展来提供数组长度作为参数:
void
printMat (int h, int w; int tab[h][w], int h, int w)
{
[code using tab[x][y]]
}
但我很快意识到我需要用指针和 malloc 来处理添加两个矩阵的“添加”函数,并且 return 无论如何都要指向一个新的 malloc 矩阵的指针……
我特别想知道,如果我不够具体,我应该如何声明参数和 return 类型,以便能够将它们用作多维数组而无需使用中间变量,同时实际传递一个指针(不管怎样,这已经是传递一个普通的多维数组作为参数做对了吗?)
好的,经过多次测试和尝试,它现在可以按我的预期工作,即使我不确定是否完全理解了所有内容,尤其是什么是指针,什么不是指针(我可能会因为试图弄清楚自己而感到困惑对于 gdb 这个,我可能应该进一步调查一个普通的单维或多维数组是否被 gdb 等视为地址),而今天我还没有得到我的 sleep/rest 和集中在最好的。
现在,我想要正确回答我最初问题的第二部分:如何 return?是否有适当的泛型类型(除了无意义的 void*
)可能适用于指向二维数组的指针(如 int(*)[][]
,但那行得通吗?)?如果太通用,转换 returned 指针的正确方法是什么,以便我可以在其上使用多维下标符号? (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;
}
和 input/output:
[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
如果要分配T
类型的缓冲区,典型的过程是
T *ptr = malloc( sizeof *ptr * N ); // sizeof *ptr == sizeof (T)
您为类型 T
.
的 N
个元素分配了足够的 space
现在让我们用数组类型替换T
,R [M]
:
R (*ptr)[M] = malloc( sizeof *ptr * N ); // sizeof *ptr == sizeof (R [M])
您为 R [M]
类型的 N
元素分配了足够的 space - IOW,您刚刚为 N
分配了足够的 space通过 M
个 R
数组。请注意,语义与上面 T
的数组完全相同;所改变的只是 ptr
的类型。
将其应用于您的示例:
int (*tab)[y] = malloc( sizeof *tab * x );
然后您可以像索引任何二维数组一样索引 tab
:
tab[x][y] = new_value();
编辑
回复评论:
yet, still, I’m not sure to understand: what’s the meaning of the “(*tab)” syntax? it’s not a function pointer I guess, but why wouldn’t *tab without parenthesis work: what’s the actual different meaning? why doesn’t it work and what does change then?
下标[]
和函数调用()
运算符的优先级高于一元*
,所以像
这样的声明
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
声明为 指针的二维数组 ,而不是指向二维数组的指针,并且对 malloc(...)
的调用无效二维数组对象的初始值设定项。
语法
int (*tab)[x][y]=malloc(x*y*sizeof(int));
将 tab
声明为 指向二维数组的指针 ,并且对 malloc
的调用是它的有效初始化程序。
但是...
使用此声明,您必须在索引之前显式取消引用 tab
,如下所示:
(*tab)[i][j] = some_value();
您没有索引到 tab
,您索引到 tab
指向 .
的内容
请记住,在 C 中,声明模仿使用 - 声明中声明符的结构与其在可执行代码中的外观相匹配。如果你有一个指向 int
的指针并且你想访问 pointed-to 值,你可以使用一元 *
运算符:
x = *ptr;
表达式*ptr
的类型是int
,所以[=43=的声明 ]写成
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
个元素(not bytes!) 来自 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();
我有一个练习,我应该使用固定大小的数组和 in/out 参数来对矩阵进行操作(add、scanf、print 等),但我想这样做在任意长度的矩阵和 return 它们上,而不是每次添加更多的 (in/)out 参数(因此可能允许更“功能”的风格)。
因为我想 return 它们,我想我可能需要 malloc 来保持内存中的数组通过函数作用域。因为我想使用多维下标符号(mat[x][y]
而不是 mat[x*len+y]
或 mat+x*len+y
)我想我应该使用某种 vla 或转换......但它似乎禁止转换为数组(但是我会经常 return 指针,如果我不能转换,如何在它们上使用下标符号?),我明显
正如编译器所说,“可能不会初始化可变大小的对象”(即使它不是直接的数组而是指向数组的指针),比如使用这种表示法:
int *tab[x][y]=malloc(x*y*sizeof(int));
如果我手动将 x 和 y 替换为 3
等常量值,我也会得到“无效的初始值设定项”。
我搜索了将近一个星期,也许那是不可能的,我应该继续前进……我还发现了这个符号,对我来说它看起来像函数指针符号,除非它是一种优先 *
运算符…
int (*tab)[x][y]=malloc(x*y*sizeof(int));
但是我不确定是否完全理解这个符号,因为然后用这种方式从 printed/filled 数组中获取随机值。
之前我曾尝试使用 VLA(可变长度数组)和 GNU 扩展来提供数组长度作为参数:
void
printMat (int h, int w; int tab[h][w], int h, int w)
{
[code using tab[x][y]]
}
但我很快意识到我需要用指针和 malloc 来处理添加两个矩阵的“添加”函数,并且 return 无论如何都要指向一个新的 malloc 矩阵的指针……
我特别想知道,如果我不够具体,我应该如何声明参数和 return 类型,以便能够将它们用作多维数组而无需使用中间变量,同时实际传递一个指针(不管怎样,这已经是传递一个普通的多维数组作为参数做对了吗?)
好的,经过多次测试和尝试,它现在可以按我的预期工作,即使我不确定是否完全理解了所有内容,尤其是什么是指针,什么不是指针(我可能会因为试图弄清楚自己而感到困惑对于 gdb 这个,我可能应该进一步调查一个普通的单维或多维数组是否被 gdb 等视为地址),而今天我还没有得到我的 sleep/rest 和集中在最好的。
现在,我想要正确回答我最初问题的第二部分:如何 return?是否有适当的泛型类型(除了无意义的 void*
)可能适用于指向二维数组的指针(如 int(*)[][]
,但那行得通吗?)?如果太通用,转换 returned 指针的正确方法是什么,以便我可以在其上使用多维下标符号? (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;
}
和 input/output:
[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
如果要分配T
类型的缓冲区,典型的过程是
T *ptr = malloc( sizeof *ptr * N ); // sizeof *ptr == sizeof (T)
您为类型 T
.
N
个元素分配了足够的 space
现在让我们用数组类型替换T
,R [M]
:
R (*ptr)[M] = malloc( sizeof *ptr * N ); // sizeof *ptr == sizeof (R [M])
您为 R [M]
类型的 N
元素分配了足够的 space - IOW,您刚刚为 N
分配了足够的 space通过 M
个 R
数组。请注意,语义与上面 T
的数组完全相同;所改变的只是 ptr
的类型。
将其应用于您的示例:
int (*tab)[y] = malloc( sizeof *tab * x );
然后您可以像索引任何二维数组一样索引 tab
:
tab[x][y] = new_value();
编辑
回复评论:
yet, still, I’m not sure to understand: what’s the meaning of the “(*tab)” syntax? it’s not a function pointer I guess, but why wouldn’t *tab without parenthesis work: what’s the actual different meaning? why doesn’t it work and what does change then?
下标[]
和函数调用()
运算符的优先级高于一元*
,所以像
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
声明为 指针的二维数组 ,而不是指向二维数组的指针,并且对 malloc(...)
的调用无效二维数组对象的初始值设定项。
语法
int (*tab)[x][y]=malloc(x*y*sizeof(int));
将 tab
声明为 指向二维数组的指针 ,并且对 malloc
的调用是它的有效初始化程序。
但是...
使用此声明,您必须在索引之前显式取消引用 tab
,如下所示:
(*tab)[i][j] = some_value();
您没有索引到 tab
,您索引到 tab
指向 .
请记住,在 C 中,声明模仿使用 - 声明中声明符的结构与其在可执行代码中的外观相匹配。如果你有一个指向 int
的指针并且你想访问 pointed-to 值,你可以使用一元 *
运算符:
x = *ptr;
表达式*ptr
的类型是int
,所以[=43=的声明 ]写成
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
个元素(not bytes!) 来自 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();