2

I want to read in a variable number of strings in C using the

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

function. So I came up with the following code:

int main() {
    int number;
    char *line;
    size_t len;
    scanf("%d", &number);
    for (int i = 0; i < number; ++i) {
        line = NULL;
        getline(&line, &len, stdin);
        printf("%s", line);
    }
}

The problem, with above code is, that the first call of getline reads in the newline character ('\n') which follows the entered number instead of the first string. Adding one of the following lines of code (denoted by OPTION 1 resp. OPTION 2) fixes the problem:

int main() {
    int number;
    char *line;
    size_t len;
    scanf("%d", &number);
    // fflush(stdin);                                            /* OPTION 1 */
        for (int i = 0; i < number; ++i) {
        line = NULL;
        getline(&line, &len, stdin);
        // while (*line == '\n') getline(&line, &len, stdin);    /* OPTION 2 */
        printf("%s", line);
    }
}

My Questions:

Is adding one of these lines (OPTION 1, OPTION 2) the correct way of doing this?

If so which one is to favor over the other?

If not what would be the correct way?

kaya3
  • 47,440
  • 4
  • 68
  • 97
SebastianWilke
  • 470
  • 1
  • 4
  • 15
  • You could just read another character after the number, or, if you don't know how many whitespaces will follow the number, you can read that as a line and then read the number from the string. – Qubit Oct 08 '18 at 11:34
  • 1
    `getline()` for the number then `sscanf()` it. Don't mess with the rest of the code :) – pmg Oct 08 '18 at 11:34
  • 1
    See https://stackoverflow.com/a/18170435/4386427 – Support Ukraine Oct 08 '18 at 11:36
  • @pmg `sscanf()` is not safe – Mayur Oct 08 '18 at 11:36
  • Option 1 is undefined behaviour. `getline()` reads an entire line from stream, storing the address of the buffer containing the text into `*lineptr`. The buffer is null-terminated and includes the newline character, if one was found. – danglingpointer Oct 08 '18 at 11:37
  • 2
    Use it safely, @Mayur, tools are as safe as you make them. Plain addition (`i + j`) is not safe and seldom used safely. – pmg Oct 08 '18 at 11:39
  • @pmg Could you please share how the `sscanf()` would look like in this case? – SebastianWilke Oct 08 '18 at 12:02
  • @SebastianWilke `if (sscanf(line, "%d", &number) != 1) /* error */;` – pmg Oct 08 '18 at 12:09

2 Answers2

4

Is adding one of these lines (OPTION 1, OPTION 2) the correct way of doing this?

OPTION 1 is undefined behavior according to the standard so I wouldn't recommend it even if it does work on your system.

OPTION 2 is better and it will "eat" the '\n' left by the scanf which is what you want. But it will also "eat" any later input just consisting of a newline.

If you just want to "eat" the newline from the input of number I'll probably do:

int main() {
   int number;
   char *line = NULL;
   size_t len = 0;
   getline(&line, &len, stdin);
   if (sscanf(line, "%d", &number) != 1)
   {
       // Illegal input
       exit(1);
   }
   for (int i = 0; i < number; ++i) {
     // -------------------    line = NULL;   Delete this - see below
     getline(&line, &len, stdin);
     printf("%s", line);
   }
   free(line);
   retur 0;
}

Notice that you shall only set line to NULL in the start of the program. Setting it to NULL in each loop causes memory leaks. Also notice the free(line)

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • Thank you for pointing the memory leak out! "Alternatively, before calling **getline()**, `*lineptr` can contain a pointer to a malloc(3)-allocated buffer `*n` bytes in size. If the buffer is not large enough to hold the line, **getline()** resizes it with realloc(3), updating `*lineptr` and `*n` as necessary." - see http://man7.org/linux/man-pages/man3/getline.3.html – SebastianWilke Oct 08 '18 at 12:26
3

The problem is with scanf.

scanf("%d", &number);

You are reading an integer from the stdin. However by hitting enter it automatically adds a "\n" at the end of your input. So once scanf is done, there's a "\n" remaining in the stdin.

Take a look at this, which gives more details: Remove \n after scanf() which read integer

To get rid of the new line after scanf, one simple solution is to add a "\n" at the end of the format string:

int main() {
   int number;
   char *line;
   size_t len;
   scanf("%d\n", &number);
   for (int i = 0; i < number; ++i) {
     line = NULL;
     getline(&line, &len, stdin);
     printf("%s", line);
   }
}

which scans for an integer followed by optional white space.

William Wu
  • 69
  • 7
  • Option 1 (using `fflush(stdin)`) is not guaranteed to work on all C implementations. The Standard [specifically gives it as an example of UB](https://port70.net/~nsz/c/c11/n1570.html#7.21.5.2p2), though for **some implementations** it is Defined Behaviour. – pmg Oct 08 '18 at 11:47
  • @pmg Do you think using `getchar()` to remove the `\n` is a better one? – William Wu Oct 08 '18 at 12:06
  • 1
    best is the stop using `scanf()` for user input. It has poor error recovery and is troublesome for unformatted user input -- *the `f` in `scanf` stands for "formatted"* :) – pmg Oct 08 '18 at 12:11