首页 > 解决方案 > 更改多维数组中的一个元素会更改所有具有值的元素

问题描述

我正在尝试编写一个程序,让用户将数据放入多维数组中,并将其显示为表格。这是我的代码的简化版本:

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

#define ROWS 5
#define COLS 5

int main(void) {
    int row, col;
    char *table[ROWS][COLS], text[11];
    for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++)
            table[i][j] = malloc(11);

    while (1) {
        printf("> ");
        scanf("%d %d %s", &row, &col, text);
        if (row > ROWS || col > COLS)
            return 1;
        table[row][col] = text;
        for (int i = 0; i < ROWS; i++) {
            for (int j = 0; j < COLS; j++)
                printf("|%10s|", table[i][j]);
            printf("\n");
        }
    }

    return 0;
}

我可以输入第一个值,它将被正确存储,如下所示:

$ ./a.out
> 0 0 foo
|       foo||          ||          ||          ||          |
|          ||          ||          ||          ||          |
|          ||          ||          ||          ||          |
|          ||          ||          ||          ||          |
|          ||          ||          ||          ||          |

但是,当我尝试输入第二个值时,第一个值也会更改:

> 0 1 bar
|       bar||       bar||          ||          ||          |
|          ||          ||          ||          ||          |
|          ||          ||          ||          ||          |
|          ||          ||          ||          ||          |
|          ||          ||          ||          ||          |

如果我输入第三个、第四个、第五个等值,这将继续;所有的值都改变了。

为什么会发生这种情况,我该如何阻止它发生?

标签: cmultidimensional-array

解决方案


当你这样做时:

table[row][col] = text;

您正在丢弃table先前从Any 循环迭代中获得的值,这malloc将使该table值指向相同的内存位置(即text)。

你要:

strcpy(table[row][col],text);

此外,您的限制检查row/col需求>=


这是更正后的代码:

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

#define ROWS 5
#define COLS 5

int
main(void)
{
    int row, col;
    char *table[ROWS][COLS], text[11];

    for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++)
            table[i][j] = malloc(11);

    while (1) {
        printf("> ");
        scanf("%d %d %s", &row, &col, text);

// NOTE/BUG: the limit check is incorrect
#if 0
        if (row > ROWS || col > COLS)
            return 1;
#else
        if (row >= ROWS || col >= COLS)
            return 1;
#endif

// NOTE/BUG: this blows away the value obtained from malloc
#if 0
        table[row][col] = text;
#else
        strcpy(table[row][col],text);
#endif

        for (int i = 0; i < ROWS; i++) {
            for (int j = 0; j < COLS; j++)
                printf("|%10s|", table[i][j]);
            printf("\n");
        }
    }

    return 0;
}

更新:

请注意,虽然上述方法有效,但它有点脆弱。

如果输入的文本行是(例如q),则程序进入无限循环。

该值11硬连线的。最好使用#definejust likeROWSCOLS

没有检查是否溢出缓冲区text

我会重新编码使用fgets,因为错误检测strtolstrsep恢复要好得多。

此外,malloc预分配一个固定的数组11。如果我们要硬连线,我们不妨放弃malloc并做:

char table[ROWS][COLS][11];

根据最终的使用情况,最初最好不要填充table,而只根据需要填充元素(即 的初始值NULL)。然后,使用 . 添加元素realloc

另外,我不确定 的格式printf是否正是您想要的(例如||)。

因此,我对代码进行了更多概括。其中一些可能会有所帮助[有些可能对您的用例来说太花哨]:

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

#define ROWS    5
#define COLS    5

int
main(void)
{
    int row;
    int col;
    int len;
    char **tp;
    char *bp;
    char *cp;
    int maxlen = 0;
    char buf[1000];
    char *table[ROWS][COLS];

    for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++)
            table[i][j] = NULL;

    while (1) {
        printf("> ");

        // get next line -- stop on EOF
        bp = fgets(buf,sizeof(buf),stdin);
        if (bp == NULL)
            break;

        // strip newline
        cp = strchr(bp,'\n');
        if (cp != NULL)
            *cp = 0;

        cp = bp;

        // get row number -- skip with garbage after it (e.g. 23x)
        row = strtol(cp,&cp,10);
        if (*cp++ != ' ')
            continue;
        if ((row < 0) || (row >= ROWS))
            continue;

        // get column number -- skip with garbage after it (e.g. 23x)
        col = strtol(cp,&cp,10);
        if (*cp++ != ' ')
            continue;
        if ((col < 0) || (col >= COLS))
            continue;

        // look for text
#ifdef WORDONLY
        // this skips internal whitespace (e.g. "hello world" --> "hello")
        bp = cp;
        cp = strsep(&bp," ");
        if (cp == NULL)
            continue;
#else
        // this preserves internal whitespace (e.g. "hello world")
        for (;  *cp != 0;  ++cp) {
            if (*cp != ' ')
                break;
        }
        if (*cp == 0)
            continue;
#endif

        // get the text length
        len = strlen(cp);
        if (len > maxlen)
            maxlen = len;

        // point to table entry
        tp = &table[row][col];

        // resize it (allowing for EOS)
        *tp = realloc(*tp,len + 1);

        // copy in the current buffer token
        strcpy(*tp,cp);

        char fmt[10];
        sprintf(fmt," %%%ds |",maxlen);

        for (int i = 0; i < ROWS; i++) {
            printf("|");
            for (int j = 0; j < COLS; j++) {
                cp = table[i][j];
                if (cp == NULL)
                    cp = "";
                printf(fmt, cp);
            }
            printf("\n");
        }
    }

    return 0;
}

推荐阅读