我正在尝试编写一个程序,让用户将数据放入多维数组中,并将其显示为表。这是我的代码的简化版本:
#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|| || || |
| || || || || |
| || || || || |
| || || || || |
| || || || || |
如果我输入第三个、第四个、第五个等值,这种情况就会继续;所有的值都被改变了。
为什么会发生这种情况,我该如何阻止它的发生?
当你这样做时:
table[row][col] = text;
您正在破坏先前从malloc
获得的table
中的值任何循环迭代都将使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
为硬接线。最好像使用ROWS
和COLS
一样使用#define
没有检查text
缓冲区是否溢出。
我建议使用fgets
、strtol
和strsep
,因为错误检测和恢复要好得多。
此外,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;
}