2

我确信这是一个非常愚蠢的问题,但它真的让我措手不及,可能是我没有正确地思考这个问题。在使用 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]);

现在,让我措手不及的是函数将其输入声明为具有特定大小的二维数组。我从来没有见过这样的做法,并且完全不确定它是否合法或者它在 Objective-C 中是如何合法的。我一直被告知要使用指向原始数组的指针并将其传递进去。但这似乎很奇怪。任何人都可以尝试向我解释这一点或指出我在扣除什么错误吗?

4

3 回答 3

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]),即指向数组的指针)。

以下是非法的,即它会产生编译器错误:

void foo(int arr[MAX_ROWS][]);
于 2013-02-13T20:57:34.070 回答
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 整数数组”。因此调整后的类型是“int (*)[Y]”或“指向 Y 个整数数组的指针”。


“调整”参数类型的规则是原始数组糟糕的几个原因之一,如果可以避免它们,则不应使用它们。不幸的是,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)编译器时,编译器会看到该函数采用 an int*(不是int[10]您编写的那个),看到​​它可以转换bar为 an int *,因此编译器不会报告错误,而是恶意地生成一个调用该函数的程序。无论您编写的类型与传递的变量不兼容,无论其主体foo()可以做类似的事情param[9] *= 2;并且应该确保这是明确定义的。

同样,foo(baz)不按值传递数组,对函数内部的数组参数所做的任何事情都直接在实际数组上完成。这与通常的 C 语义完全不同,其中所有内容都通过值/副本传递,“通过引用传递”的唯一方法是获取充当对另一个对象的引用的指针值。在这种情况下,编译器会自动为您获取一个指针,并通过引用有效地传递数组。

此外,请注意,您是否写void foo(int [10])void foo(int [100])无关紧要;在这两种情况下,参数类型都被调整为int *,因此这些声明声明了相同的函数,即使它们看起来不同。

于 2013-02-13T20:59:57.570 回答
0

为了验证上面提到的内容,我还找到了以下解释(基本上表明我在相当长的一段时间内不需要传递 2D 数组)只是迭代上面的内容。

在函数内部将一维数组声明为形式参数时,您了解到数组的实际维度是不需要的;只需使用一对空括号来通知 C 编译器该参数实际上是一个数组。这并不完全适用于多维数组的情况。对于二维数组,可以省略数组的行数,但声明中必须包含数组的列数。所以声明

int array_values[100][50]

和 int array_values[][50]

都是名为 array_values 的形式参数数组的有效声明,该数组包含 100 行乘 50 列;但声明

   int array_values[100][]

int array_values[][]

不是因为必须指定数组中的列数。

于 2013-02-14T18:29:33.090 回答