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';
    }
9
  • Any particular reason i is declared as long long in the loop but buildMap accepts an int? Commented May 18, 2017 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 for a "graphical" explanation of why. Commented May 18, 2017 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. Commented May 18, 2017 at 14:40
  • So it is not possible to modify the array through the function directly? @FedericoklezCulloca no reason, i corrected it. Commented May 18, 2017 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) Commented May 18, 2017 at 14:43

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Yes, you guessed right. I ended up using a pointer to a linear array and going through the map like` map(i*M+j)`.
@AlexandruAntochi *(map + i * M + j) is the easy, efficient, yet still very readable solution.
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 *.

7 Comments

Why use long long for the dimensions? That's not the type to be used.
Hm. Will it actually see map as 2D array? Or it will still decay to a pointer?
@Olaf Because N and M in the main function are of that type.
@EugeneSh. It should decay to long long (*map)[M].
@Someprogrammerdude: Instead of fortifying this flaw, you could have given the correct version.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.