使用带指针的二维数组的 4 种不同方式,哪个是正确的?一个解释会有很大帮助

4 different ways of using 2 D arrays with pointers , which is right? An explanation would help a lot

下面四个程序输入一个二维数组然后打印出来

恐怕我对指针工作原理的理解不如我想象的那么好。

int main(){
        int n; 
        int a [3][4];
        int i=0,j=0,count=0;
        for(i=0;i<3;i++){
            for(j=0;j<4;j++){
                scanf("%d",(a+4*i+j)); // Output-514623632  514623648   514623664   514623680   514623696   514623712   514623728   514623744   514623760   514623776   514623792   514623808   
            }
        }
        for(i=0;i<3;i++){
            for(j=0;j<4;j++){
                printf("%d\t",*(a+4*i+j));
            }
        }


        return 0;
    }

Warnings -solution.c:15:21: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int (*)[4]’ [-Wformat=]
         scanf("%d",(a+4*i+j));
solution.c:20:22: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
         printf("%d\t",*(a+4*i+j));
                 ~^     ~~~~~~~~~
                 %ls 

int main(){
    int n; 
    int a [3][4];
    int i=0,j=0,count=0;
    for(i=0;i<3;i++){
        for(j=0;j<4;j++){
            scanf("%d",*(a+4*i+j));
        }
    }
    for(i=0;i<3;i++){
        for(j=0;j<4;j++){
            printf("%d\t",**(a+4*i+j)); // Output -1 2 3 4 5 6 7 8 9 10 11 12
        }
    }


    return 0;
}

int main(){
    int n; 
    int a [3][4];
    int i=0,j=0,count=0;
    for(i=0;i<3;i++){
        for(j=0;j<4;j++){
            scanf("%d",(*(a+i)+j));
        }
    }
    for(i=0;i<3;i++){
        for(j=0;j<4;j++){
            printf("%d\t",*(*(a+i)+j));  // Output- 1 2 3 4 5 6 7 8 9 10 11 12
        }
    }


    return 0;
}

int main(){
    int n; 
    int * a=(int*)malloc(12*sizeof(int)) ;
    int i=0,j=0,count=0;
    for(i=0;i<3;i++){
        for(j=0;j<4;j++){
            *(a+4*i+j)=++count;
        }
    }
    for(i=0;i<3;i++){
        for(j=0;j<4;j++){
            printf("%d\n",*(a+4*i+j));   // Output- 1 2 3 4 5 6 7 8 9 10 11 12
        }
    }


    return 0;
}

这似乎归结为一种常见的误解阅读 "an array is a pointer" -- 这是 正确的。

在大多数情况下,数组标识符 评估为指向第一个数组元素 的指针是正确的(这不包括 &sizeof运算符)。

现在看看你的数组:

int a[3][4];

这里,a是一个3元素数组,元素类型是int ()[4]。这直接解释了为什么您的第一个示例没有按预期工作。在您的表达式中, a 计算为指向第一个元素的指针,因此类型是 int (*)[4] 而不是 只是 int * 正如您所期望的那样.因此,a+1 会将指针值的大小增加到 int 的 4 倍。有了这个解释,您应该也理解了您收到的警告。

还要注意,同理,第二个例子是错误的。添加的取消引用运算符使使用的类型正确,但您仍然访问无效位置,因为您的指针算法仍在 int (*)[4] 上运行。当然不能保证 "fail",这只是未定义的行为。

使用二维数组的唯一正确版本是第三个示例:

(a+i) 是指向 a 的第 i 个元素的指针,取消引用它会产生类型为 int ()[4] 的数组,这再次计算为指向其第一个元素的指针,所以向它添加 j 可以为您提供所需值的位置。

第四个例子不是一个二维数组,它是一个用作平面数组的动态分配,所以这里不是很有趣。如果你想动态分配一个二维数组,你必须写例如:

int (*a)[4] = malloc(3 * sizeof *a);

现在重温我的第一段:使用 int a[3][4],你有 (void *)a == (void *)*a,因为 a 的第一个元素与 [=29] 的第一个元素从相同的位置开始=],只有 a*a 类型 不同。如果数组和指针是一回事,这是不可能的。

(a+4*i+j):编译器抱怨类型,因为 a 不是 int * 类型。您将其声明为 int a[3][4],通常将其读作 3x4 维整数的二维数组。实际上,a 是一个由 3 个 4 个整数组成的数组。那么a就是第一个4个int数组的地址。请记住,定义 type v[N] 会将 v 定义为数组第一个元素的地址,并且 a 可以衰减为指向元素类型的指针。然后在你的情况下 a 衰减到指向 4 个整数数组的指针。这就是编译器抱怨 %d 需要类型为 int *.

的相应参数的原因

就算地址是对的,算起来也不是你想的那样。 a+3*i+j 表示从地址 a.

开始的第 3*i+j 个 4 整数数组的地址

你可以强制衰减到int *:

int *ta = (int *)a; // ta contains the same address but of different type...
int i=0,j=0,count=0;
for(i=0;i<3;i++){
    for(j=0;j<4;j++){
        scanf("%d",(ta+4*i+j)); // arithmetic is the right one (ints by ints)
    }
}
for(i=0;i<3;i++){
    for(j=0;j<4;j++){
        printf("%d\t",*(ta+4*i+j));
    }
}

这与第二个例子相同,算术错误。

第三条和第四条正确。第三个是因为你跳(用 i)到正确的数组,然后在这个中跳到正确的 int。

第四个和我给的代码差不多

---编辑---

正如 Felix 在评论中所说,请注意,即使这在许多常见的情况下有效 architecture/OS/compiler 也有严格的别名规则阻止您这样做。阅读他提供的 link 以获得更多信息。