C中的静态多维数组vs指针和地址

时间:2016-02-03 01:35:00

标签: c arrays pointers

我正在学习C并陷入下面的代码:

int a[NUM_ROWS][NUM_COLS], (*p)[NUM_COLS], i;
for (p = &a[0]; p < &a[NUM_ROWS]; p++) {
    (*p)[i] = 0;
}

根据作者的说法,这应该指定0到第2列的第i列。

我知道实际上它不是行和列而是连续的内存块 - 几乎是一个1d数组,其中每个元素都是1d数组。另外:数组名称相当于数组第一个元素的地址(根据我读过的书)。对于1d数组,对我来说很有意义,第一个元素可以是int或char或者其他什么。然而,在2d数组中,每个元素都是一个数组,所以同样是数组第一个元素的地址 - 这次是&#34;内部&#34;一?这是否意味着&#34; a [0]&#34;给出数组第一个元素的地址,然后我们使用&#34;&amp;&#34;操作员呢?这给了我什么,地址的地址? :/

有人可以一步一步解释这里发生的事情吗?这里的地址是什么,指针是什么等等。我在各种C书中指出了许多章节,以比较作者如何解释这一点,但看起来他们使用&#34;指针&# 34;和&#34;地址&#34;互换。

我尝试比较所有3的内容,如下:

printf("%d ", a);
printf("%d ", a[0]);
printf("%d ", &a[0]);

但它们都具有相同的值:/

3 个答案:

答案 0 :(得分:1)

你对此非常正确。请记住,a[b]相当于*(a+b),而&*x相当于x(假设x出现在表达式上下文中),因此{{1} }等同于&a[b]只是&*(a+b),因此a+b只是&a[0](同样,在表达式上下文中)。

在您的情况下,aint a[NUM_ROWS][NUM_COLS]类型的NUM_ROWS*NUM_COLS元素块。它们被分组为int NUM_ROWS元素的NUM_COLS块(行)。如果您写int,则相当于a[i][j]。内部解除引用实际上不执行内存访问,而是从数据类型中删除一定程度的取消引用。您可以将其视为类型转换。

在表达式上下文中使用时,*(*(a + i) + j)成为a指针,指向int (*)[NUM_COLS]的第一行。指针添加按a元素的大小进行缩放,asizeof(int [NUM_COLS])

在表达式上下文中使用时,您通常会看到人们谈论数组名称“衰减”为指针。在一维情况下,如果您有NUM_COLS*sizeof(int),则会“衰退”为int b[DIM],其值为int *的地址。例如,当作为函数的参数传递时,不传递数组,而是传递其地址。

在二维情况下,如果你有b,它会“衰减”成int a[NUM_ROWS][NUM_COLS]指针,这是一个指向int (*)[NUM_COLS] NUM_COLS数组的指针元件。

在您的示例中,您将以下内容传递给printf:

  1. int(衰弱到a
  2. int (*)[NUM_COLS](衰弱到a[0]
  3. int *(这只是&a[0]&*(a + 0),朽烂为a
  4. 第一个和最后一个案例基本相同。第二种情况仅在数据类型上有所不同。请注意,数据类型会影响指针添加。向指针添加int (*)[NUM_COLS]时,int将按指针所指向的大小进行缩放。

    另请注意,您的int格式并不正确,因为您要将指针值传递到预期的printf值。这不适用于所有平台,大多数编译器会警告格式字符串的错误数据类型。

    使用int格式化地址的安全方法是使用printf。这需要一个指针,因此如果指针和%p的大小不同,那么你将是安全的。

答案 1 :(得分:0)

  

我正在学习C并陷入下面的代码:

分解声明的每个部分并循环:

int a[NUM_ROWS][NUM_COLS], /* declare an array of int, size NUM_ROWS * NUM_COLS */ 
(*p)[NUM_COLS],            /* delcare pointer to array of int NUM_COLS          */
i;                         /* declare a single int           */
for (p = &a[0];            /* assign 'p' the address of 'a'  */
p < &a[NUM_ROWS];          /* while address 'p' < address a[NUM_ROWS] */
p++) {                     /* advance to next pointer 'p'    */
    (*p)[i] = 0;           /* set value at 'p[i]' = 0        */
}

因此,您需要将每个i'th整数数组中NUM_COLS列(或元素)的值设置为0。您需要初始化0 < i < NUM_COLS,否则行为将被取消定义,因为i未初始化。

一个工作示例可以帮助您逐步完成正在进行的工作。基本上,您的示例代码只是提供了一种使用指针单整数来逐步通过2D数组的方法,以隔离列值,而不是更常用的两个整数。使用两个整数(使用i作为列值将第二列设置为每行中的0),您会看到:

i = 1;
for (j = 0; j < NUM_ROWS; j++)
    a[j][i] = 0;

上面的代码段和你的代码片段都会完成同样的事情,差异是p点(保持指针值)一个3 int值的数组。您取消引用p(例如*p)以获取任何单个行的起始地址。您必须用括号括起取消引用的p以便为行中的任何单个编制索引,因为在C 运算符优先级[]运算符具有更高的值优先级高于'*'运算符。 (例如,您需要(*p)[x]而不是*p[x])。

#include <stdio.h>

#define NUM_COLS 3

int main (void) {

    int a[][NUM_COLS] = {{1,2,3},
                         {4,5,6},
                         {7,8,9}};
    int (*p)[NUM_COLS];
    int i;
    int NUM_ROWS = sizeof a/sizeof *a;

    printf ("\noriginal array:\n\n");
    for (p = a; p < &a[NUM_ROWS]; p++) {
        for (i = 0; i < NUM_COLS; i++)
            printf (" %2d", (*p)[i]);
        putchar ('\n');
    }

    /* set col 1 (2nd col) to zero */
    i = 1;
    for (p = a; p < &a[NUM_ROWS]; p++) {
        (*p)[i] = 0;
    }

    printf ("\nmodified array:\n\n");
    for (p = a; p < &a[NUM_ROWS]; p++) {
        for (i = 0; i < NUM_COLS; i++)
            printf (" %2d", (*p)[i]);
        putchar ('\n');
    }
    return 0;
}

<强>输出

$ ./bin/array_decl

original array:

  1  2  3
  4  5  6
  7  8  9

modified array:

  1  0  3
  4  0  6
  7  0  9

答案 2 :(得分:0)

array name is an equivalent of address of the first element of the array

This is not correct, although it is commonly stated. In fact, just like any other designator, a[0] designates an array of type int[NUM_COLS].

The correct version of the rule is that that the array name expession may be converted to a pointer holding the address of the first element. This happens implicitly in many uses of the array name in an expression, but there are some expressions where it does not happen.

&a[0] is an example of an expression where this conversion does not happen. It gives you the address of the entire array a[0] , in the same way that after int x; then &x gives the address of the int.

The conversion does not happen when the array expression is the operand of &, sizeof, or an increment operator.

In your printf lines, the conversion does happen in the first two, but not the third. Also, they all cause undefined behaviour due to using the wrong format specifier. Supposing you fix this, remember that when you output a pointer like this, you are only outputting part of the story; there's no indicator of the type of the pointer in the output. Although the first two cases are different pointers, they generate the same output in printf.