【问题标题】:Cannot dereference multidimensional array as expected无法按预期取消引用多维数组
【发布时间】:2019-01-09 02:57:18
【问题描述】:
int arr5[][3] = {{1,2,3}, {4,5,6}, {7,8,9}};

声明二维数组的变量包含指向第一个数组中第一个元素的指针。 arr5 被定义为每行有 3 列。 arr5 加 1 得到第二行第一个元素的地址,arr5 加 2 得到第三行第一个元素的地址。

int *row1 = arr5;
int *row2 = arr5 + 1;
int *row3 = arr5 + 2;

取消引用指向数组的指针会获取数组中的第一个元素。

printf("%d\n", *row1);  // prints 1
printf("%d\n", *row2);  // prints 4
printf("%d\n", *row3);  // prints 7

我们可以使用 [] 取消引用这些行中的元素。

for (j = 0; j < 3; j++) {
  printf("%d%c", row1[j], (j==2)?'\n':' ');
} // prints 1 2 3

我们可以不使用括号打印第二行。 Row2 现在保存第二行中第一个元素的地址。

for (j = 0; j < 3; j++) {
  printf("%d%c", *(row2+j), (j==2)?'\n':' ');
}  // prints 4 5 6

为什么我们不能在上面的例子中用 arr5+1 代替 row2? row2 保存地址 arr5+1。

for (j = 0; j < 3; j++) {
  printf("%d%c", *((arr5+1)+j), (j==2)?'\n':' ');
}  // prints what looks to be addresses

【问题讨论】:

  • int *row1 = arr5; 是个问题。 row1int*arr5 不是。 arr5(int*)[3]int *row1 = *arr5;int *row2 = *(arr5 + 1); 一样可以
  • int *row1 = arr5;没有问题。 printf("%d\n", *row1);按预期打印第一行中的第一个元素。
  • 这只是在快乐的情况下。打开编译器警告,例如-Wall -Wextra -pedantic 用于 gcc/clang 或 /W3 用于 VS。 "警告:从不兼容的指针类型初始化"
  • 当样本阵列不是方形阵列时,二维阵列问题更加清晰。考虑一个 3x4 数组。
  • 第一句错了。没有指针。该数组是 9 个连续的 int 对象。

标签: c arrays pointers multidimensional-array


【解决方案1】:

除非它是sizeof 运算符的操作数,否则_Alignof 运算符,或一元 '&amp;' 运算符,或者是使用的 字符串文字 初始化一个数组,一个类型为 "array of type" 的表达式 被转换为类型为 "pointer to type" 的表达式 指向数组对象的初始元素并且不是 左值。 C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)

因此,当您访问arr5 时,您实际上是在使用 指向 int [3] 数组的指针。当您尝试分配时:

int *row1 = arr5;

您正在尝试将类型为int(*)[3]arr5 分配给row1,即int*,从而导致警告:

"initialization from incompatible pointer type"

要消除警告,您需要取消引用arr5,从而生成与int* 兼容的int[3] 类型(访问时将其转换为指向行中第一个元素的指针)。例如,重新排列您的示例,您可以这样做:

#include <stdio.h>

int main (void) {

    int arr5[][3] = {{1,2,3}, {4,5,6}, {7,8,9}},
        *row1 = *arr5,
        *row2 = *(arr5 + 1),
        *row3 = *(arr5 + 2);
    size_t  nrow = sizeof arr5 / sizeof *arr5,
            ncol = sizeof *arr5 / sizeof **arr5;

    puts ("By row:");
    for (size_t j = 0; j < ncol; j++)       /* output row1 */
        printf ("%3d", *(row1 + j));
    putchar ('\n');

    for (size_t j = 0; j < ncol; j++)       /* output row2 */
        printf ("%3d", *(row2 + j));
    putchar ('\n');

    for (size_t j = 0; j < ncol; j++)       /* output row3 */
        printf ("%3d", *(row3 + j));
    putchar ('\n');

    puts ("\nBy array:");
    for (size_t i = 0; i < nrow; i++) {     /* output arr5 */
        for (size_t j = 0; j < ncol; j++)
            printf ("%3d", *(*(arr5 + i) + j));
        putchar ('\n');
    }
}

注意:row1[j]arr5[i][j] 通常比它们的指针符号等价物更具可读性)

使用/输出示例

$ ./bin/ptr2arrayauto
By row:
  1  2  3
  4  5  6
  7  8  9

By array:
  1  2  3
  4  5  6
  7  8  9

检查一下,如果您还有其他问题,请告诉我。

【讨论】:

  • 注意= *arr5 部分。
  • 只有在我偶然发现类似的东西的次数之后才变得更加锐利......
  • 同意,int[3]int* 的兼容性是解释等价的松散方式。通过取消引用二维数组来访问行数组本身将导致指向行的第一个元素的指针。
  • 说得好。谢谢!
【解决方案2】:

注意编译器警告。 *((arr5+1)+j) 不是int,而是int *

警告:格式“%d”需要“int”类型的参数,但参数 2 的类型为“int *”[-Wformat=]

注意:(arr5+1)+j(arr5+1+j) 相同。我怀疑 OP 想要 *(arr5+1)+j 这里。

int main() {
    int arr5[][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
    for (int j = 0; j < 3; j++) {
      printf("%d%c", *((arr5+1)+j), (j==2)?'\n':' '); // Warning here!!
    } 

    for (int j = 0; j < 3; j++) {
      printf("%d%c", *(*(arr5+1)+j), (j==2)?'\n':' ');
      //               ^ 
    }
return 0;
}

输出

-13412 -13400 -13388  UB UB UB
4 5 6                 OK OK OK

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-25
    • 2022-01-05
    • 2017-05-10
    • 2013-06-05
    • 2021-02-08
    • 2016-03-02
    • 2012-12-22
    • 2020-10-25
    相关资源
    最近更新 更多