关于指针和多维数组的混淆
如果以下情况可行:
MyFunction(int *array, int size)
{
for(int i=0 ; i<size ; i++)
{
printf(“%d”, array[i]);
}
}
main()
{
int array[6] = {0, 1, 2, 3, 4, 5};
MyFunction(array, 6);
}
为什么以下情况不可行?
MyFunction(int **array, int row, int col)
{
for(int i=0 ; i<row ; i++)
{
for(int j=0 ; j<col ; j++)
{
printf(“%d”, array[i][j]);
}
}
}
main()
{
int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
MyFunction(array, 3, 3);
}
If the following is possible:
MyFunction(int *array, int size)
{
for(int i=0 ; i<size ; i++)
{
printf(“%d”, array[i]);
}
}
main()
{
int array[6] = {0, 1, 2, 3, 4, 5};
MyFunction(array, 6);
}
Why the following is not?
MyFunction(int **array, int row, int col)
{
for(int i=0 ; i<row ; i++)
{
for(int j=0 ; j<col ; j++)
{
printf(“%d”, array[i][j]);
}
}
}
main()
{
int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
MyFunction(array, 3, 3);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
首先,一些标准语言:
给定声明,
myarray
的类型是“int
的 3 元素数组的 3 元素数组”。按照上面的规则,当您编写表达式时,
myarray
的类型会从“3元素数组的3元素数组”隐式转换(“衰减”) >int
”到“指向int
的 3 元素数组的指针”,或int (*)[3]
。因此,您的函数原型需要
注意,
int **array
与int (*array)[3]
不相同;指针算术将会不同,因此您的下标最终不会指向正确的位置。请记住,数组索引是根据指针算术定义的:a[i]
==*(a+i)
,a [i][j] == *(*(a + i) + j)
。a+i
将产生不同的值,具体取决于a
是int **
还是int (*)[N]
。这个特定的示例假设您始终传递一个 Nx3 元素的
int
数组;如果您想处理任何 NxM 大小的数组,则不太灵活。解决这个问题的一种方法是显式传递数组中第一个元素的地址,因此您只需传递一个简单的指针,然后手动计算正确的偏移量:因为我们传递了一个指向
int
的简单指针,我们不能在MyFunc
中使用双下标; arr[i] 的结果是一个整数,而不是指针,因此我们必须在一个下标操作中计算数组的完整偏移量。请注意,此技巧仅适用于真正的多维数组。现在,
**
可以表示以二维结构组织的值,但是以不同的方式构建的。例如:按照上面的规则,当表达式
d0
、d1
和d2
出现在a
的初始化器中时code>,它们的类型都从“int
的三元素数组”转换为“指向int
的指针”。类似地,当表达式a
出现在对AnotherFunc
的调用中时,其类型从“指向int
的指针的三元素数组”转换为“指向int
指针的指针”。请注意,在
AnotherFunc
中,我们为两个维度添加下标,而不是像在MyFunc
中那样计算偏移量。这是因为a
是一个指针值数组。表达式arr[i]
获取距离arr
位置的第i个指针值偏移量;然后我们找到距离该指针值的第 j 个整数值偏移量。下表可能会有所帮助 - 它显示了各种数组表达式的类型以及它们根据其声明衰减为什么(
T (*)[N]
是指针类型,而不是数组类型,因此它不衰减):高维数组的模式应该是清晰的。
First, some standard language:
Given the declaration
the type of
myarray
is "3-element array of 3-element array ofint
". Going by the rule above, when you writethe expression
myarray
has its type implicitly converted ("decay") from "3-element array of 3-element array ofint
" to "pointer to 3-element array ofint
", orint (*)[3]
.Thus, your function prototype would need to be
Note that
int **array
is not the same asint (*array)[3]
; the pointer arithmetic will be different, so your subscripts won't wind up pointing to the right places. Remember that array indexing is defined in terms of pointer arithmetic:a[i]
==*(a+i)
,a[i][j] == *(*(a + i) + j)
.a+i
will yield a different value depending on whethera
is anint **
or anint (*)[N]
.This particular example assumes you're always passing an Nx3-element array of
int
; not terribly flexible if you want to deal with any NxM-sized array. One way to get around this would be to explicitly pass the address of the first element in the array, so you're just passing a simple pointer, and then compute the proper offset manually:Since we pass a simple pointer to
int
, we can't use a double subscript inMyFunc
; the result ofarr[i]
is an integer, not a pointer, so we have to compute the full offset into the array in the one subscript operation. Note that this trick will only work for truly multidimensional arrays.Now, a
**
can indicate values that are organized in a 2-D structure, but one that was built a different way. For example:Going by the rule above, when the expressions
d0
,d1
, andd2
appear in the initializer fora
, their types are all converted from "3-element array ofint
" to "pointer toint
". Similarly, when the expressiona
appears in the call toAnotherFunc
, its type is converted from "3-element array of pointer toint
" to "pointer to pointer toint
".Note that in
AnotherFunc
we subscript both dimensions instead of computing the offset like we did inMyFunc
. That's becausea
is an array of pointer values. The expressionarr[i]
gets us the i'th pointer value offset from the locationarr
; we then find the j'th integer value offset from that pointer value.The following table might help - it shows the types of various array expressions and what they decay to based on their declarations (
T (*)[N]
is a pointer type, not an array type, so it doesn't decay):The pattern for higher-dimensional arrays should be clear.
编辑:这是我根据要求并根据您的新示例代码尝试给出的更中肯的答案:
无论数组维度如何,您传递的是“指向数组的指针” - 它是尽管指针的类型可能有所不同,但只有一个单个指针。
在第一个示例中,
int array[6]
是一个包含 6 个int
元素的数组。传递array
会传递一个指向第一个元素的指针,它是一个int
,因此参数类型是int *
,可以等效地写为int[]
。在第二个示例中,
int array[3][3]
是一个包含 3 行(元素)的数组,每行包含 3 个int
。传递array
会传递一个指向第一个元素的指针,它是一个由3个int
组成的数组。因此,类型为int (*)[3]
- 指向 3 个元素的数组的指针,可以等效地写为int [][3]
。我希望你现在就能看到其中的区别。当您传递
int **
时,它实际上是一个指向int *
数组的指针,而不是一个指向 2D 数组的指针。实际
int **
的示例如下:这里
array
是一个由 3 个int *
组成的数组,并将其传递为参数将产生int **
。原始答案:
您的第一个示例并不是真正的二维数组,尽管它的使用方式类似。在那里,您将创建
ROWS
个char *
指针,每个指针都指向不同的COLS
字符数组。这里有两个间接级别。第二个和第三个示例实际上是二维数组,其中整个 ROWS * COLS 字符的内存是连续的。这里只有一层间接。指向二维数组的指针不是
char **
,而是char (*)[COLS]
,因此您可以执行以下操作:Edit: Here's my attempt at a more to-the-point answer as requested and based on your new example code:
Regardless of the array dimensions, what you pass is a "pointer to an array" - it's only a single pointer, though the type of the pointer can vary.
In your first example,
int array[6]
is an array of 6int
elements. Passingarray
passes a pointer to the first element, which is anint
, hence the parameter type isint *
, which can be equivalently written asint []
.In your second example,
int array[3][3]
is an array of 3 rows (elements), each containing 3int
s. Passingarray
passes a pointer to the first element, which is an array of 3int
s. Hence the type isint (*)[3]
- a pointer to an array of 3 elements, which can be equivalently written asint [][3]
.I hope you see the difference now. When you pass an
int **
, it is actually a pointer to an array ofint *
s and NOT a pointer to a 2D array.An example for an actual
int **
would be something like this:Here
array
is an array of 3int *
s, and passing this as an argument would result in anint **
.Original answer:
Your first example isn't really a 2D array, although it is used in a similar way. There, you're creating
ROWS
number ofchar *
pointers, each of which points to a different array ofCOLS
characters. There are two levels of indirection here.The second and third examples are actually 2D arrays, where the memory for the entire
ROWS * COLS
characters is contiguous. There is only one level of indirection here. A pointer to a 2D array is notchar **
, butchar (*)[COLS]
, so you can do:其他人已经总结得差不多了。
int **A 表示 A 是指向数组的指针,而不是对二维数组的引用。
然而,这并不意味着它不可用。由于 C 中的数据按行优先顺序存储,因此一旦知道行长度,检索数据就应该很容易
The others have pretty much summed it up.
int **A means that A is a pointer to an array and not a reference to a 2-D array.
However, that does not mean it is not usable. Since the data in C is stored in row-major order, once you know the row length, retrieving the data should be easy
因为指针与数组指针的类型不同。有关详细信息,请参阅指向指针和指针数组的指针。
另外,这还有一些很好的信息:http://c-faq.com/aryptr/index。 html
Because a pointer pointer isn't the same type as a pointer to an array. See pointers to pointers and pointer arrays for details.
Also, this has some good info: http://c-faq.com/aryptr/index.html
第一个示例是可能的,因为当作为函数参数传递时,数组会退化为指针。
第二个示例不起作用,因为
int[3][3]
退化为int (*)[3]
,不是 双指针 <代码>int **。出现这种情况是因为二维数组在内存中是连续的,如果没有此信息,编译器将不知道如何访问第一行之后的元素。考虑一个简单的数字网格:如果我们将这些数字存储在数组
int nums[6]
中,我们将如何索引到数组中以访问元素 7?当然,通过1 * 3 + 1
,或者更一般地说,row * num-columns + column
。为了访问第一行之后的任何元素,您需要知道网格有多少列。当您将数字存储为
nums[2][3]
时,编译器将使用与手动处理一维数组完全相同的row * num-columns + column
算术,它只是对程序员隐藏。因此,在传递 2D 数组时必须传递列数,以便编译器能够执行此算术。在许多其他语言中,数组携带有关其大小的信息,从而无需在将多维数组传递给函数时手动指定维度。
The first example is possible because arrays degenerate to pointers when passed as function parameters.
The second example does not work because
int[3][3]
degenerates toint (*)[3]
, not a double pointerint **
. This is the case because 2D arrays are contiguous in memory, and without this information the compiler would not know how to access elements past the first row. Consider a simple grid of numbers:If we were storing these numbers in an array
int nums[6]
, how would we index into the array to access the element 7? By1 * 3 + 1
, of course, or more generally,row * num-columns + column
. In order to access any element past the first row, you need to know how many columns the grid has.When you store the numbers as
nums[2][3]
, the compiler uses the exact samerow * num-columns + column
arithmetic as you do manually with a 1D array, it's just hidden from the programmer. Therefore, you must pass the number of columns when passing a 2D array in order for the compiler to be able to carry out this arithmetic.In many other languages, arrays carry information about their size, eliminating the need to manually specify dimensions when passing multi-dimensional arrays to functions.
如果你想得到更中肯的答案,也许我们可以期待一个更“中肯”的问题。你的想法有两个问题:
int A[3][3]
在表达式中衰减为
其第一个元素的地址因此为
int (*)[3] 类型的指针。为了能够传递数组,您必须使用
&A[0][0]
来获取指向第一个“内部”成员的指针。A[i][j]
无法执行,因为你的编译器没有以下信息
行长,在那里。
Perhaps we can expect a more "to the point" question, if you want to have a more to the point answer. Your idea has two problems:
int A[3][3]
when usedin an expression decays to the
address of its first element thus to
a pointer of type
int (*)[3]
. To be able to pass the array through you'd have to use&A[0][0]
to get a pointer to the first "inner" member.A[i][j]
can't be performed sinceyour compiler has no information of
the row length, there.
这段代码有两个主要问题。
首先是
int **array
是错误使用的类型。这是一个指向指针的指针,而这是一个多维数组。组成这个多维数组的内存都是一个块,并且从该数组的开头到该数组的任何元素的偏移量是根据该数组中行的大小的知识来计算的。
这是一个指向整数的指针数组。指向的整数可能是内存中几个整数中的第一个,这意味着它们实际上指向整数数组。
在许多情况下,当您在程序中使用数组名称时,它的计算结果为指向数组开头的指针。如果你说:
你应该得到相同的地址打印3次,因为它们都引用相同的地址,但它们的类型不一样。最后两个的数据类型在许多用途上相似且兼容,因为 array[0] 会被视为指向 array 第一行第一个元素的指针这一行本身就是一个数组。
如果你说:
你是说有一个指向
int
的指针。虽然A[2][4]
是一个有效的表达式,但这不是一个多维数组,其方式如下:如果您说
A[1]
,则其计算结果为int *
与B[1]
类似,只不过您可以说A[1] = (int *)0x4444;
,但如果你说B[1] = (int *)0x4444;
你会得到一个编译器错误,因为B[1]
实际上是一个计算值,而不是一个变量。对于B
来说,没有int *
变量数组——只是一些基于行大小和数组第一个成员地址的计算。此代码应该执行与您想要的类似的操作(一些输出格式更改以提高可读性)。请注意 print 语句中的索引值是如何更改的。
There are two main issues with this code.
The first is that
int **array
is the wrong type to use. This is a pointer to a pointer, whileis a multidimensional array. The memory that makes up this multidimensional array is all one chunk, and the offset from the beginning of this to any element of this array is calculated based on knowledge of the size of a row in this array.
This is an array of pointers to integers. The integers that are pointed to could be the first of several integers in memory, which means that these actually point to arrays of integers.
Under many circumstances when you use the name of an array in a program it evaluates to a pointer to the beginning of the array. If you say:
You should get the same address printed 3 times because they all refer to the same address, but their types are not the same. The data type of the last two are similar and compatible for many purposes, since
array[0]
would be treated as a pointer to the first element of the first row ofarray
and this row is an array all by itself.If you say:
You are saying that there is a pointer to a pointer to an
int
. WhileA[2][4]
is a valid expression, this is not a multidimensional array in the same way as:If you say
A[1]
this evaluates to anint *
similar toB[1]
would, except that you can sayA[1] = (int *)0x4444;
, but if you saidB[1] = (int *)0x4444;
you would get a compiler error becauseB[1]
is actually a calculated value, not a variable. WithB
there is no array ofint *
variables -- just some calculations based on the row size and the address of the very first member of the array.This code should do something similar to what you wanted (some output formatting changes for readability). Note how the index value in the print statement is changed.