2

I am trying to take the input of the following type:

5 4
a a a a
a b a p
c d e a
d b c a
d h i k

Using the following code:

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

int main()
{
    int m, n;
    scanf("%d %d", &m, &n);
    char mat[m][n], str[2 * n];
    for (int i = 0; i < m; i++)
    {
        fgets(str, 2 * n, stdin);
        //char d;
        //getc(d); // added to absorb if any char remains in stream after pressing enter
        int j = 0;
        while (j < n)
        {
            mat[i][j] = str[2 * j];
            j++;
        }
    }
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
            printf("%c ", mat[i][j]);
        printf("\n");
    }
    return 0;
}

The program doesn't take the second line of input as when I press enter after it just prints the output and exists. When I run using GDB it shows Invalid Parameter passed to C runtime function. I have tried searching it and tried by adding getc after fgets but no success. I am unable to debugg the program.

Please help me. It would be nice if we can make a generic program for the following type of input.

5 4
a ar a alp
atg bk a p
cf dg er alphs
a g hdb gdb
klat cmat s fun

Thanks in advance.

  • 1
    Your `getc` call after `fgets` doesn't really make sense. It could read the first character of the *next* line, making your program "loose" it. Instead make sure that `str` is large enough to securely fit the full line, including the newline and the null-terminator. And why are you using a `while` loop instead of a `for` loop? – Some programmer dude Jan 23 '22 at 08:37
  • @Someprogrammerdude I added this because before adding ```getc```, I was getting the same problem. Thanks for pointing out. I have edited the question to include the point. – user17762868 Jan 23 '22 at 08:39
  • Thinking about it, with the input you show (e.g. `a a a a`) then with `getc` is *should* work. The input is seven character plus a newline. You read those seven characters, and `fgets` add the null-terminator. *But* because the length is too small to fit the newline it will be left unread for the next call to `fgets` (which will read it and you will get an "empty" line). *With* the `getc` call you read that newline, and the next `fgets` call should read the next line proper. Better solution is to make sure the buffer is large enough to fit the full line, including newline, and not have `getc`. – Some programmer dude Jan 23 '22 at 08:43
  • @Someprogrammerdude the reason for using while is that I want to process the input further more in some manner on the fly as soon as it gets into the ```mat``` but I haven't included that here. It would haven't made sense as the error remains same. :( – user17762868 Jan 23 '22 at 08:45
  • So use e.g. `3 * n` as the size, and use `sizeof str` in the `fgets` call. See e.g. [here](https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input) for how to remove the trailing newline in a safe way. – Some programmer dude Jan 23 '22 at 08:46
  • @Someprogrammerdude ```But because the length is too small to fit the newline``` what does it mean? Here ```n=4``` so fgets gets 8 chars long memory which I thing could accommodate ```\n``` or ```\0```. I have tried the ```2*n+10``` but it doesn't work. – user17762868 Jan 23 '22 at 08:59
  • The [`fgets`](https://en.cppreference.com/w/c/io/fgets) function will read up to the provided size *minus one* to fit the null-terminator. Because the line is *8* character including the newline, `fgets` will not be able to read the newline, and leave it in the input buffer buffer for next input operation. So e.g. `2 * n + 10` should be enough to fit the whole line, including the newline (***if*** the input is well-formed). – Some programmer dude Jan 23 '22 at 10:11
  • And have you tried to *debug* your program? For example by using a debugger to step through your code statement by statement while monitoring variables and their values, to see what really happens? Pay close attention to the contents of the first line you read... Think about what is in the input buffer after the first and initial `scanf` call... – Some programmer dude Jan 23 '22 at 10:18

2 Answers2

1

As pointed out by @Someprogrammerdude, your code leaves '\n' in the input stream, and when you enter the for loop, it first takes '\n' from the stream and then takes the other input. So, your code breaks before taking the last line of the input.

One of the possible solutions could be:

Since you know how to use fgets(), I think you should use fgets() to get the first line too. Then use strtok() to retrieve integers from the string using strtol(). However, this is not a memory-efficient and time-efficient solution.

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

int main()
{
int m,n;
char *str = (char*)malloc(23*sizeof(char));
//23 = 2*10+1+2, here 10 is the maximum length of two integers,
// 1 is for space and 2 chars to accommodate '\n' and NULL.
fgets(str, 23, stdin);
char *tk = strtok(str, " ");
m = (int)strtol(tk, (char **)NULL, 10);
tk = strtok(NULL, " ");
n = (int) strtol(tk, (char **)NULL, 10);
str = (char *)realloc(str, (2*n+1)*sizeof(char));
char mat[m][n];
/*
Your code
*/
return 0;
}
Kumar
  • 173
  • 1
  • 12
  • 1
    *"However, this is not a memory-efficient and time-efficient solution."*, what makes you think this? Also, why are you using `realloc`? Why are you using `strtok` instead of using `sscanf` to parse the two integers out of the first line? And why aren't you indenting your code? :-) – hyde Jan 24 '22 at 06:03
  • @hyde I am using ```realloc``` as the input ```n``` could be greater than or equal to 13 which makes 2*13>23 this would not fit the OP input string in the second line of input. – Kumar Jan 24 '22 at 06:08
  • @hyde Further honestly, I am also not that expert so I don't know how to use ```sscanf``` for the above task as you pointed out. I have known strtok from [this answer](https://stackoverflow.com/questions/70106043), I asked sometime before. Thanks for the comment. I will look into ```sscanf````. :) – Kumar Jan 24 '22 at 06:13
  • 1
    `int parsed = sscanf(str, "%d%d", &m, &n); if (parsed != 2) { exit(1); }` – hyde Jan 24 '22 at 06:57
0

Use getline to read a line. gets is deprecated after c11.

@Some programmer dude: Actually, getline is not a standard C function, it's a POSIX (Linux, macOS, etc.) function. Which means it's not available for Windows.

See this post to understand difference of gets and getline.

cin.getline() reads input up to '\n' and stops

cin.gets() reads input up to '\n' and keeps '\n' in the stream

If you debug your program you can see gets called in each 2 loops:

5  4
=======i is 0
=======i is 1
a a a a
=======i is 2
=======i is 3
a b a p
=======i is 4

So program works but you need discard \n (and/or \r ). Also use gets as you said to surpass first \n. So your code can be as

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

int main()
{
    int m, n, len;
    scanf("%d %d", &m, &n);
    char mat[m][n], str[2*n];
    fgets(str, 2 * n, stdin);
    for (int i = 0; i < m; i++)
    {
        printf("=======i is %d\n", i);
        //fgets(str, 2 * n, stdin);
        getline(&str, &len, stdin);
        int j = 0;
        while (j < n)
        {
            mat[i][j] = str[2 * j];
            j++;
        }
    }
    printf("=======OUTPUT======");
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
            printf("%c ", mat[i][j]);
        printf("\n");
    }
    return 0;
}

output will be as :

4 5 
=======i is 0
s s s s
=======i is 1
d d d d 
=======i is 2
f f f f
=======i is 3
 g g g g
Majid Hajibaba
  • 3,105
  • 6
  • 23
  • 55