为什么这两个简单的代码会给出不同的结果
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
但我不确定它对解决方案有多大改进!
代码 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
但我不确定它对解决方案有多大改进!