为什么这两个简单的代码会给出不同的结果

Why these 2 simple codes give different results

代码 1

#include <stdio.h>
   
//Compiler version gcc  6.3.0

int main()
{
  int a[10][10];
  int *p;
  p=a;
  int temp;
  for(int j=0;j<9;j++){
    scanf("%d",(p+j));
  }
  for(int j=0;j<3;j++){
    for(int k=0;k<3;k++){
      if(k>j){
        temp = a[j][k];
        a[j][k] = a[k][j];
        a[k][j] = temp;
      }
    }
  }
  for(int j=0;j<9;j++)
  printf("%d ",*(p+j));
  return 0;
}

代码 2

#include <stdio.h>

//Compiler version gcc  6.3.0

int main()
{
  int a[3][3];
  int *p;
  p=a;
  int temp;
  for(int j=0;j<9;j++){
    scanf("%d",(p+j));
  }
  for(int j=0;j<3;j++){
    for(int k=0;k<3;k++){
      if(k>j){
        temp = a[j][k];
        a[j][k] = a[k][j];
        a[k][j] = temp;
      }
    }
  }
  for(int j=0;j<9;j++)
  printf("%d ",*(p+j));
  return 0;
}

为什么改变数组范围会得到不同的结果?我正在转置这些代码中的矩阵。我的输入是 1 2 3 4 5 6 7 8 9 第一个代码的输出没有给出转置,但是第二个代码的输出给出了正确的结果,分别是 1 4 7 2 5 8 3 6 9

对于初学者,编译器应该针对此代码片段发出一条消息

int *p;
p=a;

因为赋值语句的左操作数和右操作数的类型不同,没有从一个到另一个的隐式转换。即左操作数的类型为 int *,而右操作数的类型为 int ( * )[10],因为数组指示符 a 隐式转换为指向其第一个元素的指针。

你必须写

p = ( int * )a;

所以指针p指向二维数组第一个“行”的第一个元素a并且这个循环填充了第一个“行”的9个元素。

for(int j=0;j<9;j++){
  scanf("%d",(p+j));
}

实际上这个循环和下面的循环是一样的

for(int j=0;j<9;j++){
  scanf("%d", a[0] + j);
}

所有其他元素都具有不确定的值。

在第二种情况下,二维数组恰好有 9 个 int.

类型的元素

所以相应的循环用值填充数组的所有元素。

for(int j=0;j<9;j++){
  scanf("%d",(p+j));
}

a[3][3]的内存布局是:

low address     ----------------------------->    high address
[0][0] [0][1] [0][2] [1][0] [1][1] [1][2] [3][0] [3][1] [3][2]

a[10][10] 布局因此

low address     ------------------------------->    high address
[0][0] [0][1] [0][2] [0][3] [0][4]...[9][6] [9][7] [9][8] [9][9]

您的输入和输出循环从 低地址 索引数组,访问前 9 个元素,对于 a[10][10] 即:a[0][0]a[0][8] - 第 0 行的前 9 个元素,它们不是转置所作用的元素(这是前三行的前三个元素)。

换个角度来看,对于 10x10 数组:

// Referenced in the transposition
   0123456789
0: ###.......
1: ###.......
2: ###.......
3: ..........
4: ..........
5: ..........
6: ..........
7: ..........
8: ..........
9: ..........

// Referenced in the input/output:
   0123456789
0: #########.
1: ..........
2: ..........
3: ..........
4: ..........
5: ..........
6: ..........
7: ..........
8: ..........
9: ..........

两者之一(显而易见且直观):

for( int i = 0; i < 3; i++ )
{
    for( int j = 0; j < 3; j++ )
    {
        scanf( "%d", a[i][j] ) ; 
    }
}

或(坦率地说是奇怪和不明智的):

for( int j = 0; j < 9; j++ )
{
    scanf( "%d", p +                         // Low address +
                 ((j / 3) * sizeof(*a) +     // row offset +
                 (j % 3)  ) ;                // column
}

输出循环也是如此。

请注意,p 是多余的。你同样可以:

(int*)a +                   // Low address +
((j / 3) * sizeof(*a) +     // row offset +
(j % 3)  ) ;                // column

但我不确定它对解决方案有多大改进!