0

Here is my code which tries to take the no. of lines(n) and no of
characters(m) in each line as input. But while running it takes n and m then doesn't take any input after that. The expected input may also contain spaces

 #include<stdio.h>
 int main() 
 {
    int n,m,i,j;
    scanf("%d%d",&n,&m);
    char **a=(char**)malloc(n);
    for(i=0;i<m;i++)
    a[i]=(char*)malloc(m+1);
    for(i=0;i<n;i++)
    scanf("%[^\n]%*c", a[i]);
 }
/*An example of input would be:
4 4
####
#S #
## #
#E #
*/
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • 1
    Run it in a debugger to see line by line what the program is doing. No need to turn to Stackoverflow so quickly. – kaylum Sep 21 '15 at 02:34
  • 2
    `scanf("%[^\n]%*c", a[i]);` fails if the line begins with `'\n'`. Just read the line with `fgets()` and then process the data read. – chux - Reinstate Monica Sep 21 '15 at 02:44
  • 2
    the posted code is C, not C++, please remove the C++ tag, – user3629249 Sep 21 '15 at 02:44
  • 2
    You have the `C` and `C++` tags, but this appears to be C code. What are you looking for? – Beta Sep 21 '15 at 02:44
  • replace the scanf with fgets() and after each successful call to fgets(), replace the newline, if any, with '\0' – user3629249 Sep 21 '15 at 02:46
  • 3
    1) do not cast the returned value from malloc() because it creates maintenance headaches and is not needed because the type of the returned value is `void *` which can be assigned to any pointer. 2) always check (!=NULL) the returned value to assure the operation was successful. – user3629249 Sep 21 '15 at 02:48
  • 1
    And you have a memory leak. `free()` the memory when you're finished with it. – owacoder Sep 21 '15 at 03:01
  • And you forgot to include `stdlib.h`. – Spikatrix Sep 21 '15 at 03:32

2 Answers2

0

Loosely corrected

//    scanf("%d%d",&n,&m);  change to 
      scanf("%d %d%*c",&n,&m); // better suggestion

Considering input format this is necessary

and

// char **a=(char**)malloc(n);  change to
char **a=malloc(n * sizeof( char *) );

and, for some reason m and in below loops are kind of mixed

  for(i=0;i<m;i++) 
       a[i]=(char*)malloc(m+1);
  for(i=0;i<n;i++)
   scanf("%[^\n]%*c", a[i]);

change to

for(i=0;i<n;i++) { // for n pointers
     a[i] =  malloc ( m * sizeof(char) ); // allocate memory
     fgets( a[i], m, stdin );  // scan string
     // optionally add this line
     //a[ strlen(a[i]) - 1 ]  = '\0'; // null terminate optionally
    a[i][strcspn(a[i], "\n")]  = '\0'; // another awesome suggestion
  }

size of m is the key here when scanning string, make sure enough bytes are availale specially for '\n' and '\0' worth allocating actual memory + 2

asio_guy
  • 3,667
  • 2
  • 19
  • 35
0

You ask "But what's wrong with my code while handling input?" in the comments. A fair question. What you have received in answer to your question are the suggestions, from experience, of how to avoid the pitfalls of trying to do line-oriented input with scanf. The bottom line is that unless you have a very certain and guaranteed input, then using scanf for line oriented input is usually the wrong tool for the job. Why? Because any variation in your input can lead to matching failures with scanf, where using other tools would have no problem.

Does that mean scanf is bad an shouldn't be used? Of course not, it means that for line-oriented input, the better choice is usually a line-oriented libc function like fgets or getline. Even character-oriented functions like getchar() or fgetc can provide a great deal of flexible input handling. That said, scanf does have its place, but like everything else, it has its pros and cons. Just weight them against what you need with your job.

On to your code. What's wrong? There are a lot of little things that have been pointed out in the comments. The biggest is char **a=(char**)malloc(n);. You only allocate n bytes of storage, NOT n pointers. Each pointer is 4 or 8 bytes (32/64 bit boxes - in general). So your allocation needs at minimum:

char **a = malloc (n * sizeof *a);  /* don't cast the return */

Next issue, you are attempting to fill the lines in a for loop. While you may have received some number n, there is no guarantee you will actually have that many lines of data to read. If you don't, then you are forcing the code to read and allocate for input that doesn't exist. A while loop may fit a little better here.

When you allocate memory, you need to preserve a pointer to the start of each allocation so you can free it when it is no longer needed. Just get in the habit of tracking the memory you allocate and freeing it when you are done with it. While it is freed when you exit, when you begin writing functions that allocate memory, your code will leak like a sieve if you are not in the habit of managing it properly.

Now, with the caveat that we all understand that using fgets or getline is the preferred method, and that lines beginning with a newline will be an issue, you can write your code to use scanf and allocate storage manually for the strings, in a reasonable manner that has just a few additions to your original code. Take a look and let me know if you have questions.

#include <stdio.h>
#include <stdlib.h>

/* validate memory allocation succeeded */
void checkalloc (void *p)
{
    if (!p) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        exit (EXIT_FAILURE);
    }
}

/* empty any characters remaining in input buffer */
void emptyinputbuffer (FILE *fp) 
{
    int c;
    while ((c = fgetc(fp)) != '\n' && c != EOF) {}
}

int main (void) 
{
    int n, m, i;
    n = m = i = 0;  /* always initialize your variables */

    if (scanf ("%d%d",&n,&m) != 2) {    /* scanf has a return -- use it */
        fprintf (stderr, "error: failed to read 'm' & 'n'\n.");
        exit (EXIT_FAILURE);
    }
    emptyinputbuffer (stdin);   /* remove trailing newline from input buffer */

    char **a = malloc (n * sizeof *a); /* you need sizeof to allocate 8 bytes */
    checkalloc (a);                    /* always validate every allocation    */

    /* allocate storage for first string and validate */
    a[i] = malloc (sizeof **a * (m + 1)); /* you could omit sizeof here, =1   */
    checkalloc (a[i]);

    while (scanf("%[^\n]%*c", a[i]) == 1) /* while instead, lines maybe less  */
    {
        if (++i == n) break;

        a[i] = malloc (sizeof **a * (m + 1));
        checkalloc (a[i]);
    }

    if (i < n) n = i;   /* save the number of lines actually read */

    for (i = 0; i < n; i++) /* print them out */
        printf (" line[%2d] : %s\n", i, a[i]);

    for (i = 0; i < n; i++) /* free allocated memory */
        free (a[i]);
    free (a);

    return 0;
}

Input

$ printf "4 4\n####\n#5 #\n## #\n#E #\n"
4 4
####
#5 #
## #
#E #

Compile

gcc -Wall -Wextra -o bin/scanf_loop scanf_loop.c

Output

$ printf "4 4\n####\n#5 #\n## #\n#E #\n" | ./bin/scanf_loop
 line[ 0] : ####
 line[ 1] : #5 #
 line[ 2] : ## #
 line[ 3] : #E #

Memory Use Check

$ printf "4 4\n####\n#5 #\n## #\n#E #\n" | valgrind ./bin/scanf_loop
==11065== Memcheck, a memory error detector
==11065== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==11065== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11065== Command: ./bin/scanf_loop
==11065==

 line[ 0] : ####
 line[ 1] : #5 #
 line[ 2] : ## #
 line[ 3] : #E #
==11065==
==11065== HEAP SUMMARY:
==11065==     in use at exit: 0 bytes in 0 blocks
==11065==   total heap usage: 5 allocs, 5 frees, 52 bytes allocated
==11065==
==11065== All heap blocks were freed -- no leaks are possible
==11065==
==11065== For counts of detected and suppressed errors, rerun with: -v
==11065== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thank you so much David, I really appreciate your effort! You took me into the depth of things and i understood what mistakes i had and what rules to follow. Really happy to get that solution! :) – Shubham Java Sep 21 '15 at 12:27
  • Just one little question.. If i had to take input in competitive programming questions what approach should i use? – Shubham Java Sep 21 '15 at 12:28
  • It would depend on the line length and what parsing you needed to do to get what you needed out of the line. In this case, with such a short line and no parsing, either `fgetc()` or `getchar` to read and store the 4 chars in each line would be about as fast as anything else. If the lines were greater than 32 characters long, then look at `fgets` or `getline`. There are virtually no circumstances where `scanf` would perform better simply due to the variadic nature of the function. I have written both `getchar` and `getline` variations that perform equally well. – David C. Rankin Sep 21 '15 at 12:43
  • If you are looking at maximizing speed, look at `getchar_unlocked` and `fgetc_unlocked` versions of the functions which provide slight speed advantages over their non `_unlocked` counterparts. (see: `man 3 getchar_unlocked` for more details) – David C. Rankin Sep 21 '15 at 12:45