简体   繁体   English

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

[英]Static mutlidimensional arrays in C vs pointers and addresses

I'm learning C and got stuck on the following piece of code: 我正在学习C并陷入下面的代码段:

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

According to the author this is supposed to assign 0 to i-th column of a 2d array. 根据作者,这应该将0分配给2d数组的第i列。

I understand that in reality it's not rows and columns but a continuous block of memory - pretty much a 1d array where each element is a 1d array. 我知道实际上不是行和列,而是连续的内存块-几乎是一个1d数组,其中每个元素都是一个1d数组。 Also: array name is an equivalent of address of the first element of the array (according to the book I'm reading). 另外:数组名称等效于数组的第一个元素的地址(根据我正在阅读的书)。 For 1d arrays it makes sense to me, 1st element can be an int or char or whatever. 对于一维数组,这对我来说很有意义,第一元素可以是int或char或其他类型。 In 2d arrays however, each element is an array, so again an address of the first element of the array - this time the "inner" one? 但是,在2d数组中,每个元素都是一个数组,因此数组的第一个元素的地址也是-这次是“内部”一个吗? Does that mean "a[0]" gives the address of the 1st element of an array, and then we use the "&" operator on it? 这是否意味着“ a [0]”给出了数组第一个元素的地址,然后在其上使用“&”运算符? What does that give me, an address of an address? 地址给我一个什么地址? :/ :/

Could someone please explain what's happening here, step-by-step? 有人可以一步一步解释这里发生了什么吗? What's an address here, what's a pointer, etc. I went over a number of chapters on pointers in various C books to compare how authors explain this, but it looks like they use "pointer" and "address" interchangeably. 这里的地址是什么,指针是什么,等等。我遍历了各种C语言书籍中有关指针的许多章节,以比较作者的解释方式,但是看起来他们可以互换使用“指针”和“地址”。

I tried comparing the contents of all 3, like so: 我尝试比较所有3个内容,如下所示:

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

but it all had the same value :/ 但它们都具有相同的值:/

You're pretty much correct about this. 您对此非常正确。 And remember, a[b] is equivalent to *(a+b) , and &*x is equivalent to x (assuming x appears in an expression context), so &a[b] is equivalent to &*(a+b) which is just a+b , so &a[0] is just a (again, in an expression context). 记住, a[b]等效于*(a+b)&*x等效于x (假设x出现在表达式上下文中),所以&a[b]等效于&*(a+b)只是a+b ,所以&a[0]只是a (再次在表达式上下文中)。

In your case, int a[NUM_ROWS][NUM_COLS] is a block of NUM_ROWS*NUM_COLS elements of type int . 在您的情况下, int a[NUM_ROWS][NUM_COLS]是一块int类型的NUM_ROWS*NUM_COLS元素。 They are grouped into NUM_ROWS blocks (rows) of NUM_COLS int elements. 它们分为NUM_COLS int元素的NUM_ROWS个块(行)。 If you write a[i][j] it's equivalent to *(*(a + i) + j) . 如果写a[i][j]等效于*(*(a + i) + j) The inner dereference doesn't actually perform a memory access, but rather it removes a level of dereference from the data type. 内部取消引用实际上并不执行内存访问,而是从数据类型中删除了一定程度的取消引用。 You can think of it as a type cast. 您可以将其视为类型转换。

When used in an expression context, a becomes an int (*)[NUM_COLS] pointer which points to the first row of a . 当在表达式上下文中使用时, a变为指向a的第一行的int (*)[NUM_COLS]指针。 Pointer addition scales by the size of an element of a , which is sizeof(int [NUM_COLS]) which is NUM_COLS*sizeof(int) . 指针加法根据a元素的大小缩放,即sizeof(int [NUM_COLS]) NUM_COLS*sizeof(int)NUM_COLS*sizeof(int)

You will commonly see people talk of array names "decaying" into pointers when used in an expression context. 您通常会看到人们谈论在表达式上下文中使用数组名称时,它们会“衰减”为指针。 In the one-dimensional case, if you have int b[DIM] , it "decays" into an int * whose value is the address of b . 在一维情况下,如果您有int b[DIM] ,它将“分解”为一个int *其值为b的地址。 For instance, when passed as an argument to a function, the array isn't passed, but instead its address is passed. 例如,当作为参数传递给函数时,不传递数组,而是传递其地址。

In the two-dimensional case, if you have int a[NUM_ROWS][NUM_COLS] , it "decays" into an int (*)[NUM_COLS] pointer, which is a pointer to an array of NUM_COLS int elements. 在二维情况下,如果您具有int a[NUM_ROWS][NUM_COLS] ,则它将“衰减”为一个int (*)[NUM_COLS]指针,该指针是NUM_COLS int元素数组的指针。

In your example, you pass following to printf: 在您的示例中,将以下内容传递给printf:

  1. a (decays to int (*)[NUM_COLS] ) a (对int (*)[NUM_COLS]衰减)
  2. a[0] (decays to int * ) a[0] (对int *衰减)
  3. &a[0] (this is just &*(a + 0) or a , decayed to int (*)[NUM_COLS] ) &a[0] (这只是&*(a + 0)a ,衰减为int (*)[NUM_COLS]

The first and last cases are basically the same. 第一种情况和最后一种情况基本相同。 The second case differs only in data type. 第二种情况仅在数据类型上有所不同。 Note that the data type affects pointer addition. 请注意,数据类型会影响指针添加。 When adding an int to a pointer, the int is scaled by the size of whatever the pointer points to. 在将int添加到指针时, int会根据指针指向的大小进行缩放。

Also note that your printf formats aren't really correct, since you're passing pointer values where int values are expected. 还要注意,您的printf格式并不是真正正确的,因为您是在需要int值的地方传递指针值。 That won't work on all platforms, and most compilers will warn about bad data types for the format string. 并非在所有平台上都适用,大多数编译器都会警告格式字符串的数据类型错误。

The safe way to format an address with printf is with %p . 使用printf格式化地址的安全方法是使用%p This expects a pointer, so you will be safe if a pointer and an int don't have the same size. 这需要一个指针,因此如果指针和int的大小不相同,您将很安全。

I'm learning C and got stuck on the following piece of code: 我正在学习C并陷入下面的代码段:

Break down each part of the declaration and loop: 分解声明的每个部分并循环:

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        */
}

So you will set the value of the i'th column (or element) in each array of NUM_COLS ints to 0 . 因此,您将每个NUM_COLS ints数组中的i'th列(或元素)的值设置为0 You need to initialize 0 < i < NUM_COLS or the behavior will be undefined as i is uninitialized. 您需要初始化0 < i < NUM_COLS否则行为将是不确定的,因为i 0 < i < NUM_COLS初始化。

A working example may help you step through what is taking place. 一个有效的示例可以帮助您逐步了解正在发生的事情。 Basically, your example code is simply providing a way to step through a 2D array using a pointer and a single integer to isolate a column value rather than the more common use of two integers. 基本上,示例代码只是提供一种使用指针单个整数来隔离列值的方法来逐步浏览2D数组,而不是更常见的使用两个整数。 Using two integers (using i as the column value to set the second column to 0 in each of the rows) you would see: 使用两个整数(使用i作为列值将每一行中的第二列设置为0 ),您将看到:

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

Both the code snippet above and your code snippet will accomplish the same thing, the difference being p points (holds the pointer value to) an array of 3 int values. 上面的代码段和您的代码段都将完成相同的操作,不同之处是p指向3个int值的数组(将指针值指向)。 You dereference p (eg *p ) to get the beginning address of any individual row. 您取消引用p (例如*p )以获得任何单独行的起始地址。 You must surround the dereferenced p with parenthesis in order to index any individual value in the row because in C operator precedence the [] operator has higher precedence than the '*' operator. 您必须用括号将解引用的p括起来,以便为行中的任何单个建立索引,因为在C 运算符优先级中[]运算符的优先级高于'*'运算符。 (eg you need (*p)[x] instead of *p[x] ). (例如,您需要(*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;
}

Output 输出量

$ ./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

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] . 实际上,与任何其他指示符一样, a[0]指示类型为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. &a[0]是不进行此转换的表达式的示例。 It gives you the address of the entire array a[0] , in the same way that after int x; 它以int x;之后的相同方式为您提供整个数组a[0]的地址int x; then &x gives the address of the int . 然后&x给出int的地址。

The conversion does not happen when the array expression is the operand of & , sizeof , or an increment operator. 当数组表达式是&sizeof或增量运算符的操作数时,转换不会发生。

In your printf lines, the conversion does happen in the first two, but not the third. 在您的printf行中,转换确实发生在前两个中,但没有发生在第三个中。 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. 尽管前两种情况是不同的指针,但它们在printf中生成相同的输出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM