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

时间:2017-05-18 14:37:45

标签: c arrays pointers segmentation-fault

我试图将从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';
    }

2 个答案:

答案 0 :(得分:1)

请记住,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时,我不对数组使用address-of运算符。那是因为数组自然地衰减到指向第一个元素的指针。通过,例如&line在语义上不正确,因为它会将char (*)[M]类型的内容传递给函数,而不是char *

答案 1 :(得分:1)

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

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

program screenshot

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

long long M; char line[M];

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

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

另一个问题是使用“long long ** map”。虽然它不会导致complie失败,但它也不会给你预期的结果。当数组的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("\r\n\r\n");

  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("\r\n");
  }

  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)  

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