c - 将 2D 数组传递给函数时出现分段错误



我正在尝试将从stdin读取的字符串中的值直接写入数组,但是我遇到了分段错误。由于数组是在我读取 N 和 M 后声明的,因此应该已经分配了内存,对吧?

int main()
{
long long N;
long long M;
scanf("%lld%lld",&N,&M);
char line[M];
long long map[N][M];
for (long long i=0; i<M; i++)
{
scanf("%s", &line);
buildMap(&map, i, &line);
}
for (long long i=0; i<N; i++)
for (long long j=0; j<M; j++)
printf(&map);
}

void buildMap(long long **map, long long i, char * line)
{
for (long long j=0; j<strlen(line); j++)
{
map[i][j] = line[j]-'0';
}

我已经阅读了您的代码,我假设您正在尝试通过用户输入构建 2D 地图,这是一个字符串(在您的代码中名为"Line"),应该只包含从 0 到 9 的数字。从 0 到 9 的数字可能表示地图的不同元素。我猜对了吗?

我复制并修改了您的代码,最后我设法得到了这样的结果:

程序截图

如果我猜对了,让我先解释一下您的代码无法成功遵守的原因。

long long M; char line[M];

在这里,您使用了一个变量来声明数组的大小。此语法适用于其他一些编程语言,但不适用于 C。在 C 语言中,在编译源代码时,编译器必须确切地知道为每个函数(在您的情况下为 main() 函数)分配多少堆栈内存空间。由于编译器在尝试编译代码时不知道数组有多大,因此会出现编译失败。

一种常见的解决方案是,我们选择将数组存储在堆中,而不是将数组存储在堆栈中,因为堆内存是在程序运行时动态分配和释放的。换句话说,您可以在获取用户输入后决定要分配的内存量。函数 malloc() 和 free() 用于此类操作。

另一个问题是使用"长长**地图"。虽然它不会导致合规失败,但它也不会给你预期的结果。当数组的 M(数组宽度)是已知的常量值时,我们总是使用 "long long map[][M]" 作为参数。但是,在您的情况下,由于 M 未被选中,常见的解决方案是手动计算目标位置,因为数组中的元素始终以线性顺序存储在内存中,而不管数组尺寸如何。

我已经修复了上述两个问题,我正在粘贴下面修改后的源代码,已成功执行:

#include <malloc.h>
#include <string.h>
void buildMap(int *map, int i, char * line);
int main()
{
int N;
int M;
scanf("%d%d", &N, &M);
/*Since M (available memory space for "Line") is set by user, we need to build
"szSafeFormat" to restrict the user's input when typing the "Line". Assuming M
is set to 8, then "szSafeFormat" will look like "%7s". With the help of 
"szSafeFormat", the scanf function will be scanf("%7s", Line), ignoring 
characters after offset 7.*/
char szSafeFormat[256] = { 0 };
sprintf(szSafeFormat, "%%%ds", M - 1);
//char line[M];
char *Line          = (char *)malloc(sizeof(char) * M);   //raw user input
char *pszValidInput = (char *)malloc(sizeof(char) * M);   //pure numbers
//long long map[N][M];
int *pnMap = (int *)malloc(sizeof(int) * M * N);
memset(pnMap, 0xFF, M * N * sizeof(int));   //initialize the Map with 0xFF
for (int i = 0; i < /*M*/N; i++)
{
scanf(szSafeFormat, Line);              //get raw user input
sscanf(Line, "%[0-9]", pszValidInput);  //only accept the numbers
while (getchar() != 'n');              //empty the stdin buffer
buildMap((int *)(pnMap + i * M), i, pszValidInput);
}
printf("rnrn");
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
//if the memory content is not 0xFF (means it's a valid value), then print
if (*(pnMap + i * M + j) != 0xFFFFFFFF)   
{
printf("%d", *(pnMap + i * M + j));
}
}
printf("rn");
}
free(Line);
free(pszValidInput);
free(pnMap);
return 0;
}
void buildMap(int *map, int i, char * line)
{
for (int j = 0; j < strlen(line); j++)
{
(int) *((int *)map + j) = line[j] - '0';
}
}

我使用类型"int"而不是"long long",但是如果您坚持继续使用"long long",应该没有任何问题。如果继续使用"long long",则打印出数组值时的条件应从:

if (*(pnMap + i * M + j) != 0xFFFFFFFF)

if (*(pnMap + i * M + j) != 0xFFFFFFFFFFFFFFFF)  

还有一些关于用户输入验证的其他修改,我在代码中写了一些额外的注释。

请记住,C 支持可变长度数组(您已经在使用的东西)。这意味着您实际上可以将维度作为参数传递给函数,并在数组参数的声明中使用它们。也许像

void buildMap(const size_t N, const size_t M, long long map[N][M], long long i, char * line) { ... }

呼叫喜欢

buildMap(N, M, map, i, line);

请注意,我已将NM的类型更改为size_t,这是用于可变长度数组维度的正确类型。您应该相应地更新变量声明,并对scanf格式字符串使用"%zu


请注意,在对buildMap的调用中,我不对数组使用地址运算符。这是因为数组自然衰减到指向其第一个元素的指针。通过例如&line在语义上是不正确的,因为它会将char (*)[M]类型的内容传递给函数,而不是char *

相关内容

  • 没有找到相关文章