2

I'm trying to write the values from the string that is read from stdin directly into the array, but I get a segmentation fault. Being that the array is declared after I read N and M, the memory should already be allocated, right?

  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';
    }
Alexandru Antochi
  • 1,295
  • 3
  • 18
  • 42
  • Any particular reason `i` is declared as `long long` in the loop but `buildMap` accepts an `int`? – Federico klez Culloca May 18 '17 at 14:40
  • 2
    An array of arrays is not the same as a pointer to pointer. See e.g [this old answer of mine](http://stackoverflow.com/questions/18440205/casting-void-to-2d-array-of-int-c/18440456#18440456) for a "graphical" explanation of why. – Some programmer dude May 18 '17 at 14:40
  • 2
    `long long **map` is not a 2D array and it can't be used to point to one. It does not contain any information about the array dimensions, so the 2D indexing is just not possible. – Eugene Sh. May 18 '17 at 14:40
  • So it is not possible to modify the array through the function directly? @FedericoklezCulloca no reason, i corrected it. – Alexandru Antochi May 18 '17 at 14:42
  • Of course it is possible. But not like this. Just pass a pointer to it along with the dimensions. Then use them to calculate the correct index. (Or cast the pointer to VLA with these dimensions) – Eugene Sh. May 18 '17 at 14:43
  • Pass a 2D array to the function, not a pointer to pointer! – too honest for this site May 18 '17 at 14:47
  • Place a declaration `void buildMap(long long **map, long long i, char * line);` before your `main()`, and enable compiler warnings, and your compiler will almost certainly emit warnings and error messages explaining the problem. Essentially, the arguments your code passes to functions are of different types than the functions expect - and that causes undefined behaviour if the function does almost anything with those arguments. – Peter May 18 '17 at 14:48
  • Note: the type for array dimensions is `size_t`, not `long long`! And this is not valid C. You missed a declaration before the usage of a function. Also `int main()` should be `int main(void)`; C is not C++! – too honest for this site May 18 '17 at 14:48
  • Possible duplicate of [Passing a 2D array of structs](http://stackoverflow.com/questions/35614862/passing-a-2d-array-of-structs) – too honest for this site May 18 '17 at 14:56

2 Answers2

1

Remember that C supports variable-length arrays (something which you already use). That means you can actually pass the dimensions as arguments to the function and use them in the declaration of the array argument. Perhaps something like

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

Call like

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

Note that I have changed the type of N and M to size_t, which is the correct type to use for variable-length array dimensions. You should update the variable-declarations accordingly as well as use "%zu for the scanf format string.


Note that in the call to buildMap I don't use the address-of operator for the arrays. That's because arrays naturally decays to pointers to their first element. Passing e.g. &line is semantically incorrect as it would pass something of type char (*)[M] to the function, not a char *.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

I have read your codes, and I assume you are attempting to build a 2D map via user input, which is a string (named "Line" in your code) that should only contains numbers from 0 to 9. Numbers from 0 to 9 may represent different elements of the map. Am I guessing right?

I copied and modified your code, and finally I managed to get a result like this:

program screenshot

If I am guessing right, let me first explain the reasons why your code can not be successfully complied.

long long M; char line[M];

In here you have used a variable to declare the size of an array. This syntax works in some other programming languages, but not in C. In C, when compling the source code, the compiler must know exactly how much stack memory space to allocate for each function (main() function in your case). Since the complier does not know how large the array is when it is trying to complie your code, you get a compling failure.

One common solution is that, instead of storing array in stack, we choose to store array in heap, because the heap memory is dynamically allocated and released when the program is running. In other words, you can decide how much memory to allocate after you get the user input. Function malloc() and free() are used for this kind of operation.

Another problem is using "long long **map". Though it will not cause complie failure, it won't give you the expected result either. When the M (array width) of the array is a known constant value, we always perfer using "long long map[][M]" as the parameter. However, in your case, with M being unkown, the common solution is to manually calculate the target location, since the elements in an array are always stored in a linear order in memory, regardless of the array demension.

I have fixed the aforementioned two problems, and I am pasting the modified source code below, which has been successfully complied:

#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';
  }
}

I used type "int" instead of "long long", but there should not be any problems if you insist to continue using "long long". If you continue to use "long long", the condition while printing out the array values should be changed from:

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

to

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

There are also some other modifications regarding user input validation, with which I have written some addtional comments in the code.

zhugen
  • 240
  • 3
  • 11