我确信这是一个非常愚蠢的问题,但它真的让我措手不及,可能是因为我没有正确地思考这个问题。在使用OpenGL ES 2.0时,我遇到了以下代码行
/////////////////////////////////////////////////////////////////
// Forward declarations
static void SceneMeshInitIndices(
GLushort meshIndices[NUM_MESH_INDICES]);
static void SceneMeshUpdateNormals(
SceneMeshVertex mesh[NUM_MESH_COLUMNS][NUM_MESH_ROWS]);
static void SceneMeshUpdateMeshWithDefaultPositions(
SceneMeshVertex mesh[NUM_MESH_COLUMNS][NUM_MESH_ROWS]);
现在,让我措手不及的是,函数将其输入声明为具有特定大小的2D数组。我从来没有见过这样做过,我完全不确定它是否合法或者它在Objective-C中是如何合法的。我总是被告知要使用指向原始数组的指针并将其传入。但是,这看起来很奇怪。任何人都可以尝试向我解释这一点,或指出我推断的错误吗?
答案 0 :(得分:7)
当将数组传递给函数时,它们会根据一组规则衰减为指针。这些是:
一。间接的第一级(即,数组的第一维/索引)衰减为指针,因此不需要指定其大小(也不使用它)。
两个。阵列的其他尺寸保持不变,并且不会衰减成指针。需要指定它们,它们是参数类型的一部分。
这就是为什么以下内容基本上是等同的和合法的:
void foo(int arr[]);
void foo(int arr[MAX_ARRAY_SIZE]);
void foo(int *arr);
但以下不等同,尚未合法:
void foo(int arr[MAX_ROWS][MAX_COLUMNS]);
void foo(int **arr);
以下是同等和合法的:
void foo(int arr[MAX_ROWS][MAX_COLUMNS]);
void foo(int arr[][MAX_COLUMNS]);
(这些被解释为int (*arr)[MAX_COLUMNS])
,即作为指向数组的指针。
以下是非法, i。即它会生成编译器错误:
void foo(int arr[MAX_ROWS][]);
答案 1 :(得分:1)
我总是被告知要使用指向原始数组的指针并将其传递给它。
您被告知使用指向原始数组元素的指针。你已经被告知这个因为如果你试图声明一个按值获取数组的函数,那么语言指定参数类型被'调整'以获取指向元素类型的指针(有效地导致数组参数类型忘记它的大小)并通过引用而不是通过值传递)。因此,基本上你被告知要手动进行调整,以便源代码准确,明确地表示实际的真相。
您展示的声明也可以使用此建议声明:
static void SceneMeshInitIndices(GLushort *meshIndices);
static void SceneMeshUpdateNormals(SceneMeshVertex (*mesh)[NUM_MESH_ROWS]);
static void SceneMeshUpdateMeshWithDefaultPositions(SceneMeshVertex (*mesh)[NUM_MESH_ROWS]);
应该很清楚,int [X][Y]
的类型意味着'Y个数组的X个数组'。所以元素类型是'Y ints数组'。所以调整后的类型是'int(*)[Y]'或'指向Y int数组的指针。'
“调整”参数类型的规则是原始数组很糟糕的几个原因之一,如果可以避免它们则不应该使用它们。不幸的是,C并没有一个很好的方法来做到这一点,但是C ++有std::array
,它的行为与数组一样;即你可以按值传递它们,你可以按值返回它们,它们不会自动转换为指向元素类型的指针(所以它们永远不会忘记它们的大小)等等。
也许更多的例子会让它更清晰。如果你写了:
void foo(int param[10]);
并且您不知道调整参数类型的规则然后您可能希望以下代码产生错误:
int bar[5];
foo(bar); // error? an array of 5 ints is not the expected type.
对于这段代码:
int baz[10];
foo(baz);
您可能希望将数组bar
复制到参数中,并且对参数所做的任何修改都不会对原始数组产生任何影响。不幸的是,这些完全合理的期望是错当您编写void foo(int param[10]);
时,编译器会看到带有数组类型的参数,并将其修改为与您编写void foo(int *param);
因此,当您编写foo(bar)
时,编译器会看到该函数采用int*
(而不是您编写的int[10]
),看到它可以将bar
转换为int *
因此编译器恶意生成调用该函数的程序,而不是报告错误。无论您编写的类型与传递的变量不兼容,无论foo()
的主体是否可以执行类似param[9] *= 2;
的操作,并且应该确保这是明确定义的。
类似地,foo(baz)
不按值传递数组,并且对函数内部的数组参数所做的任何操作都直接在实际数组上完成。这完全不同于通常的C语义,其中所有内容都通过值/副本传递,并且“通过引用传递”的唯一方法是获取作为对另一个对象的引用的指针值。在这种情况下,编译器会代表您自动获取指针,并通过引用有效地传递数组。
此外,请注意,您是否撰写void foo(int [10])
或void foo(int [100])
并不重要;在这两种情况下,参数类型都调整为int *
,因此这些声明声明了相同的函数,即使它们看起来不同。
答案 2 :(得分:0)
为了验证上面已经注意到的内容,我还发现了以下解释(基本上说我不需要在很长一段时间内传递一个2D数组),它只是迭代上面的内容。
当在函数内声明一维数组作为形式参数时,您了解到不需要数组的实际维度;只需使用一对空括号来通知C编译器该参数实际上是一个数组。这在多维数组的情况下并不完全适用。对于二维数组,可以省略数组中的行数,但声明必须包含数组中的列数。所以声明
int array_values[100][50]
和 int array_values [] [50]
都是一个名为array_values的形式参数数组的有效声明 100行乘50列;但声明
int array_values[100][]
和
int array_values[][]
不是因为必须指定数组中的列数。