这些数组在C指针表示法中发生了什么?

时间:2011-08-21 23:37:16

标签: c pointers

我对指针数组有点困惑,我只是想确保我是对的。

当我写int *arr时,它只是一个指向int变量的指针,而不是一个数组。只是我初始化它(比如说malloc)它变成了一个数组。我到目前为止对吗?

另外我还有另外一个问题:给了(在学校里)一个小功能,应该返回一系列成绩,第一个单元格是平均值。这个功能是刻意错误的:他们做的是设置

int *grades = getAllGrades();

并且他们已经将指针减少了一个平均'cell'

*(grades - 1) = getAverage();
 return *(grades - 1)

我知道这是错误的,因为返回的值不是数组,我只是不知道如何解释它。当我设置指针时,机器/编译器如何知道我是否只需要指针或数组?

(如果我不清楚它,因为我试图询问一些对我来说仍然含糊不清的事情,我道歉)

4 个答案:

答案 0 :(得分:5)

  

机器/编译器如何知道我是否只需要一个指针或一个   阵列

它没有,它永远不会。假设你

int *a = malloc(3 * sizeof(int));

你刚刚分配了12个字节(假设int是4)。但malloc只能看到12.这是一个大对象还是很多小对象?它不知道。实际上唯一知道的就是你;)

现在谈谈你的具体例子,

int *grades = getAllGrades();

此时,正如您所说,没有什么可说的grades是否指向数组。但知道它指向一个数组,这才是最重要的。或者,也许你知道它并没有指向数组。关键是你必须知道getAllGrades做了什么,知道它是返回一个数组还是一个指向一件事的指针。

*(grades - 1) = getAverage();
return *(grades - 1)

这不一定是错的,但它确实看起来像草图。如果它是一个数组,您可能希望grades[0] == *(grade + 0)成为第一个元素,因此grades[-1] == *(grades - 1)看起来就像在第一个元素之前。再次,它不一定是错的;也许他们在getAllGrades做了:

int* getAllGrades() {
    int *grades = malloc(sizeof(int) * 10);
    return grades + 1;
}

也就是说,他们将启动计算为1.已知会发生这种情况(参见C中的数字食谱),但这有点奇怪。

答案 1 :(得分:3)

数组不是指针。指针不是数组。

也许更清楚地说数组对象不是指针对象,反之亦然。

当您声明int *arr时,arr是一个指针对象。这就是全部;它不可能,也永远不会是一个数组。

当你执行arr = malloc(10 * sizeof *arr);时,(如果malloc()没有失败,你应该经常检查),arr现在指向一个int对象。该对象恰好是int的10元素数组的第一个元素(由malloc调用创建的元素)。请注意,有一个指向数组的指针,但这不是它。

在非常真实的意义上,数组不是C中的第一类类型。您可以像使用任何其他类型的对象一样创建和操作数组对象,但是您很少会处理数组值< / em>直接。相反,您将通过指向这些元素的指针间接处理数组对象的元素。在上面的arr声明的情况下,您可以对指向第一个元素的指针执行算术运算以获取指向其他元素的指针(并且您必须有一些其他机制来记住有多少元素)。

在大多数上下文中,任何数组类型的表达式都会隐式转换为指向数组第一个元素的指针(例外是:一元&运算符的操作数,sizeof的操作数operator,以及用于初始化数组(子)对象的初始值设定项中的字符串文字。这就是使看起来的规则,好像数组和指针是可以互换的。

数组索引操作符[]实际上定义为处理指针而不是数组。 a[b]只是撰写*((a)+(b))的另一种方式。如果a碰巧是数组对象的名称,那么它首先被转换为指针,如上所述。

我强烈建议您阅读comp.lang.c FAQ的第6部分。 (该链接指向首页,而不是直接指向第6部分,因为我喜欢鼓励人们浏览整个内容。)

我提到有数组指针。给定int foo[10];&foo[0]是指向int的指针,但&foo是指向整个数组的指针。两者都指向内存中的相同位置,但它们的类型不同,并且在指针算法下它们的行为完全不同。

答案 2 :(得分:0)

  

当我写int *arr时,它只是指向int变量的指针,而不是   一个数组呢。只是我初始化它(用malloc说)   它变成了一个阵列。我到目前为止对吗?

嗯,是的,没有:)。 arr是指向某个内存块的指针(或地址)。在arr初始化之前,它可能指向无效或无意义的内存地址。因此,将arr视为指向int变量的指针可能会令人困惑。例如,在malloc之前,您不能在它指向的位置存储整数。

此外,如果您说mallocarr 指向到数组后,它可能会更容易理解“变成”< / em>一个数组。在malloc之前,arr指向某个随机的非感知位置。

  

当我设置指针时,机器/编译器如何知道我是否需要   只是指针或数组?

如果设置指针(例如arr = <something>),则只需更改指针指向的位置。那可能就是你想要的。如果不想更改arr点的位置,但是您想要更改存储在其指向的内存中的值,则必须一次执行一个元素(例如,使用for循环遍历每个元素在数组中)。

答案 3 :(得分:0)

你是对的,数组和指针之间的唯一区别是约定。下面是getAllGrades()函数返回时内存必须是什么样子的图片:

                          | secret malloc() stuff |
                          +-----------------------+
                          | average value         |
                          +-----------------------+  ----\
grades* points here --->  | grade at index 0      |      |  by convention
                          +-----------------------+      |  this stuff is
                          | grade at index 1      |      |  called grades[]
                          +-----------------------+      |
                          | grade at index 2      |      |
                          +-----------------------+      .
                          | ...                   |      .

现在,数组和指针之间没有区别。因此,当编译器看到*(grades - 1)时,它首先从成绩指针中减去1。这是特殊的指针算法,因此它知道向上移动一整个int块,并指向平均值。然后它可以对此值进行操作,例如使用*(grades - 1) = getAverage()设置平均值。

抛开数组索引:数组索引的编译方式与指针算法完全相同。例如,grades[2]被编译为*(grades + 2),它将指针算术向下移动2个块到我图片中标记为“索引2处的等级”的内存地址。这意味着您可以将*(grades - 1) = getAverage()更改为grades[-1] = getAverage(),它的工作原理完全相同。

如果你想进行实验,你可以做(-1)[grades]编译为*(-1 + grades)并且也可以正常工作,但那是愚蠢的,所以不要这样做:)