C、二维数组,如何在所有 8 个方向上查找特定数字

C, 2D Array, How to find specific number in all 8 directions

您只能输入 0 和 1 作为数组中的值。然后代替所有值为 0 的元素,在所有八个方向上输入该元素周围值为 1 的元素的数量。最后打印二维数组,将 1 的值更改为 *.

这是一个示例:

这是我的代码

#include <stdio.h>
#include <stdlib.h>

int main()
{
int n,m;
scanf("%d%d", &n, &m);
int a[n][m];
for(int i=0; i<n; i++){
    for(int j=0; j<m; j++){
        scanf("%d", &a[i][j]);
        if(a[i][j]==1){
            a[i][j]=9;
        }
    }
}
for(int i=0; i<n; i++){
    for(int j=0; j<m; j++){
        int counter=0;
        if(a[i][j]==9){
            printf("*\t");
        }else if(a[i][j]==0){
        if(a[i+1][j+1]==9 && i!=n && j!=m) counter++;
        if(a[i-1][j-1]==9 && i!=0 && j!=0) counter++;
        if(a[i+1][j-1]==9 && i!=n && j!=0) counter++;
        if(a[i-1][j+1]==9 && i!=0 && j!=m) counter++;
        if(a[i][j+1]==9 && j!=m) counter++;
        if(a[i][j-1]==9 && j!=0) counter++;
        if(a[i+1][j]==9 && i!=n) counter++;
        if(a[i-1][j]==9 && i!=0) counter++;
        a[i][j]=counter;
        counter=0;
        printf("%d\t", a[i][j]);
        }
    }
    printf("\n");
 }

return 0;
}

不确定问题是什么,但代码中存在错误 - 例如,您正在通过以下方式访问数组边界:

if(a[i-1][j-1]==9 && i!=0 && j!=0)

当 i 和 j 为零时,您正在读取 a[-1][-1] 的值。改变条件的顺序并首先检查 i 和 j 将防止这种访问。 当您访问矩阵的“边界”时,也会发生同样的情况。 而且,“i!=n && j!=m”条件在循环中始终为真 - 只要 i

但除此之外,其中一些访问实际上是从 数组中读取的,但是从错误的位置读取的。 下面是一个示例:

给定2x2的矩阵

0 1
1 0

这是您的代码生成的结果:

2   *
*   3

显然,'3' 是错误的。

为什么会这样? 二维数组实际上是作为一维数组存储在内存中的,像这样:

0  1  1  0  // this is your data

0  1  2  3  // this is the index in the array (offset from the array start)

并且每个元素都位于从数组开始的 row*columns+column 的偏移量处(即这是在给定 i 和 j 的一维数组中查找索引的公式)。 当您计算最后一个元素(本例中的 a[1][1])的“1”邻居时,您计算两个明显的结果(在 a[1][0] 和 a[0][1]) ,但随后您遇到了这种情况:

if(a[i-1][j+1]==9 && i!=0 && j!=m)

对于最后一个单元格,i 为 1,j 为 1,因此条件的第二部分为真。 但是 a[i-1][j+1]==9 呢? 这引用了 row*columns+column 处的内存,对吧? IE。 (i-1)*2+(j+1),因为那是您尝试访问的内容。将 i 和 j 替换为 1 和 1,您将在上面的一维数组中得到偏移量 2。 这是输入矩阵中的 1(或者实际上是 9,因为你已经替换了它)。 所以你数了两次。

更好地防止“跳出”矩阵(但不一定跳出实际数组)将解决此错误。

您有 UB(未定义行为)。

与(例如):

if (a[i + 1][j + 1] == 9 && i != n && j != m)

然后,a 总是 获取,即使 ij 超出范围。

要解决此问题,请重新排序条款:

if (i != n && j != m && a[i + 1][j + 1] == 9)

现在,如果 ij 超出范围,则 if 将被“短路”:(即 _short 电路评估)和 a 不会 被提取。

但是,您的程序输出不正确。您正在手写所有内容。这使得调试变得困难(即哪些行有错误)。

没有明确描述的是,您在给定零点附近的 3x3 内核中对 non-zero 值求和。

硬编码缩放 不太好。假设内核必须是 100x100。我们会编写 10,000 行代码吗?


如果我们重构使用 for 循环,调试起来会容易得多。并且,使用更具描述性的名称而不是 i/j/m/n:

n       hgt
m       wid
i       y*
j       x*

代码如下:

#include <stdio.h>
#include <stdlib.h>

int
main(int argc,char **argv)
{
    int hgt, wid;
    int counter;
    FILE *xf;

    --argc;
    ++argv;

    if (argc > 0)
        xf = fopen(*argv,"r");
    else
        xf = stdin;
    if (xf == NULL) {
        perror(*argv);
        exit(1);
    }

    fscanf(xf,"%d%d", &hgt, &wid);
    int a[hgt][wid];

    for (int ycur = 0; ycur < hgt; ycur++) {
        for (int xcur = 0; xcur < wid; xcur++) {
            int val;
            fscanf(xf,"%d", &val);

            if (val == 1)
                val = 9;

            a[ycur][xcur] = val;
        }
    }

    for (int yctr = 0; yctr < hgt; yctr++) {
        for (int xctr = 0; xctr < wid; xctr++) {
            if (a[yctr][xctr] == 9) {
                printf("*\t");
                continue;
            }

            counter = 0;
            for (int yoff = -1; yoff <= 1; yoff++) {
                int ycur = yctr + yoff;

                if (ycur < 0)
                    continue;
                if (ycur >= hgt)
                    continue;

                for (int xoff = -1; xoff <= 1; xoff++) {
                    int xcur = xctr + xoff;

                    if (xcur < 0)
                        continue;
                    if (xcur >= wid)
                        continue;

                    counter += (a[ycur][xcur] == 9);
                }
            }

            printf("%d\t",counter);
        }

        printf("\n");
    }

    return 0;
}

这是您的示例输入的程序输出:

*   1   1   2   *
2   2   2   *   3
*   1   2   *   2