为什么我的程序输出错误的值?

时间:2017-12-14 13:24:46

标签: c debugging

我最近一直在学习内存分配和指针,我制作了这个程序,要求用户输入矩阵的尺寸并输入它的元素,然后以矩阵格式显示元素。这是我输入的代码。

#include"stdio.h"
#include"stdlib.h"

int *minput();

int *minput(int x,int y)
{
    int *M;
    M=(int*)malloc(x*y*sizeof(int));
    for(int i=0;i<=(x-1);i++)
    {
        for(int j=0;j<=(y-1);j++)
        {
            printf("A(%d,%d)=",i+1,j+1);
            scanf("%d",(M+i+j));
        }
    }
    return M;
}

int main()
{
    int *A,a,b;
    printf("Matrix is (m*n)\n\n");
    printf("m=");
    scanf("%d",&a);
    printf("n=");
    scanf("%d",&b);
    A=minput(a,b);
    printf("\n");

    for(int k=0;k<=(a-1);k++)
    {
        for(int l=0;l<=(b-1);l++)
        {
            printf("%d ",*(A+k+l));
        }
        printf("\n");
    }
    free(A);
    return 0;
}

然而,当我提供输入时,我得到了这个:

Matrix is (m*n)

m=3
n=3
A(1,1)=1
A(1,2)=2
A(1,3)=3
A(2,1)=4
A(2,2)=5
A(2,3)=6
A(3,1)=7
A(3,2)=8
A(3,3)=9

1 4 7
4 7 8
7 8 9

出了什么问题?我不应该

1 2 3
4 5 6
7 8 9

我的代码中有什么问题吗?

3 个答案:

答案 0 :(得分:4)

输出错误,因为*(A+k+l)不是matrix[k][l]访问矩阵元素的正确方法。

用于寻址存储为&#34; flat&#34;数组需要将其中一个索引的值乘以相反维度的大小。根据您乘以的索引,您可以获得行主要订单或列主要订单。

您需要对(M+i+j)函数内的minput应用相同的修补程序。

// Input
scanf("%d",(M+y*i+j));
...
// Output
printf("%d ",*(A+b*k+l));

k乘以b背后的想法是让k&#34;计数更快&#34;。对于k 1的每个增量,您需要跳过整行的b元素。在您的三列矩阵示例中,如果您想要访问每行的矩阵的A[k][0](初始列)中的元素,则您的索引将计为三:0,{{1} },3。这是通过将6乘以k来实现的。其余部分与通常的指针算法相同:b等同于*(A+b*k+l)

Demo.

答案 1 :(得分:3)

让我们仔细看看你的一个循环(我使用输入循环,但错误也在输出循环中):

for(int i=0;i<=(x-1);i++)
{
    for(int j=0;j<=(y-1);j++)
    {
        printf("A(%d,%d)=",i+1,j+1);
        scanf("%d",(M+i+j));
    }
}

外循环的第一次迭代i为零。然后内循环运行,我们读入(反过来)M+0+0M+0+1M+0+2

然后我们运行外​​部循环的第二次迭代,其中内部循环将读入M+1+0M+1+1M+1+2

在外循环的这两次迭代中,您将读入M+1M+2 两次。那是因为M+0+1M+1+0 是相同的元素

要解决此问题,让我们看一下你在内存中的“矩阵”

+---+---+---+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
+---+---+---+---+---+---+---+---+---+

里面的数字是索引。现在,012是第一行。 345是第二个。 678是最后一个。

从这里我们可以看到,为了到达下一行,我们必须添加列数。

从这里我们得到公式line * number_of_columns + column来获得索引。

将它放在适当位置,您的输入循环可能看起来像

for(int i=0;i<x;i++)
{
    for(int j=0;j<y;j++)
    {
        printf("A(%d,%d)=",i+1,j+1);
        scanf("%d",M+i*y+j);
    }
}

答案 2 :(得分:1)

要选择写入值的地址,请使用:

M+i+j

让我们尝试使用(i,j)的几个值:

0,0  -> M + 0
0,1  -> M + 1
0,2  -> M + 2  // So far so good.
1,0  -> M + 1  // not right
1,1  -> M + 2  // not right
... etc.

你想要M + (i * x) + j。对于x == 3

0,0  -> M + 0
0,1  -> M + 1
0,2  -> M + 2  
1,0  -> M + 3  
1,1  -> M + 4  
... etc. 

从同一存储器读取时,指针算法也是如此。

此外,由于每次指针只上升一次,你可以得到相同的行为:

int *m = M;
for(int i=0;i<x;i++)
{
    for(int j=0;j<=(y-1);j++)
    {
        printf("A(%d,%d)=",i+1,j+1);
        scanf("%d",m);
        m++;
    }
}

甚至:

for(int i=0; i<x*y; i++) {
   printf("A(%d,%d)=", i/3, i%3);
   scanf("%d", M + i);
}

其他要点:

在一种方法中,您使用变量x,y,i,j,在另一种方法中使用a,b,k,l。我假设你已经这样做了,因为你不想用另一个覆盖一个。但由于范围,这不是一个因素。 x是函数minput()的本地函数 - 您可以在x中拥有另一个main(),它们将完全相互独立。在这两个地方使用x,y,i,j,因为它们是维度和循环计数器的“合理”名称。

for(i=0; i<x; i++)是循环x次的传统方式。你的i<=(x-1)是等同的但是混乱和混乱。

现在不鼓励投射malloc()的结果。 Do I cast the result of malloc?

int *M = malloc(x*y*sizeof(int));