0

The problem is about taking input of strings x number of times using an array of pointers. x is the value entered by the user. I wrote the following code for the same. But the program is taking only x-1 inputs. I have inserted fflush(stdin) because I think the scanf is consuming an enter first but I don't know from where.

I have tried using gets but with no use.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    //code to take input in an array of pointers
    int x,i,j,length;
    char ch[50],*t;
    printf("How many names you want to sort:\n");
    scanf("%d",&x);
    char *names[x];
    char *p;
    printf("Enter the names:\n");
    for(i=0;i<x;i++)
    {
        fflush(stdin);
        scanf("%[^\n]s",ch);
        length = strlen(ch);
        p = (char *)malloc((length+1) * sizeof(char));
        strcpy(p,ch);
        names[i] = p;
    }
    return 0;
}
Sukrit Kapil
  • 103
  • 6
  • 4
    The C specification explicitly mentions that calling `fflush` on an input-only stream (like `stdin`) leads to *undefined behavior*. Some implementations add it as a non-portable extension though. To solve that problem with your code, remove the call to `fflush` and modify the following `scanf` to `scanf(" %49[^\n]",ch)`. Note the leading space, which tells `scanf` to read and ignore all leading white-space. – Some programmer dude Jun 14 '19 at 07:45
  • Besides the above problem, you might notice something else in my variant of the `scanf` call: No `"s"` in the format. There's no `%[...]s` format specifier, it's just `%[...]`. – Some programmer dude Jun 14 '19 at 07:46
  • @Someprogrammerdude I did not get you completely. Particularly the %49[^\n] part. I have'nt read anything like that. Can you explain a bit more. – Sukrit Kapil Jun 14 '19 at 07:48
  • The `49` part tells `scanf` to read at most 49 characters. Plus the null-terminator that equal 50 characters, which is the size of the array `ch`. This is so you don't write beyond the end of the array. The rest is just the same as before, but with a leading space which (as I explained) drops all leading white-space, and the missing `s` at the end which shouldn't be there. – Some programmer dude Jun 14 '19 at 07:52
  • @Someprogrammerdude OK got you. But in a book it was mentioned that use gets() for multi word strings but if you want to use scanf write it in %[^\n]s format. – Sukrit Kapil Jun 14 '19 at 07:56
  • 2
    If your book tells you to use `gets`, a function that's [dangerous](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used) and have even been removed from the C standard, then I really recommend that you throw it away and get a new book. Even more if it tells you to use a `scanf` format that doesn't exist. – Some programmer dude Jun 14 '19 at 07:57
  • It is probably a really dated book, or it is just a basic introduction book that does not care about security – Maxim Irrigad Jun 14 '19 at 08:03
  • 1
    have a look at `fgets`, which is built for reading in strings line by line. – Stephan Lechner Jun 14 '19 at 08:04
  • @SukritKapil Is the book "Let us C"? – klutt Jun 14 '19 at 08:13
  • @klutt yup buddy! any thoughts? – Sukrit Kapil Jun 14 '19 at 17:51
  • @SukritKapil See my answer. I edited it. And burn that book. – klutt Jun 14 '19 at 18:19

1 Answers1

4

Why bother with complex format strings if you don't have to? Use fgets.

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

void err(const char * msg) {
    fprintf(stderr, msg);
    exit(1);
}

int main()
{
    int x,i;
    char ch[50];
    printf("How many names you want to sort:\n");
    if(!fgets(ch, 50, stdin)) err("Error reading line");
    if(sscanf(ch, "%d",&x) != 1) err("Could not read integer");

    // Better than using VLA
    char **names = malloc(x*sizeof(*names));
    if(!names) err("Error allocating names");

    printf("Enter the names:\n");
    for(i=0;i<x;i++) {
        if(!fgets(ch, 50, stdin)) err("Error reading line");
        ch[strcspn(ch, "\n")] = 0; // Remove newline
        if(!(names[i] = strdup(ch))) err("Error duplicating string");
    }

    for(int i=0; i<x; i++)
        printf("name %d: %s\n", i, names[i]);
}

Whenever a function has a return value that may indicate an error you should ALWAYS check it, and here that is the case for malloc, fgets, strdup and sscanf and. Read the documentation to find out what it actually returns to see how to check for errors. sscanf returns the number of successful assignments, and the other three returns a pointer which is NULL on failure.

You wrote in the comments that you are learning from the book "Let us C". A better fitting title would be "How to not code C". I've had a quick look at it and, it is really really bad. Apart from teaching very outdated C, it also teaches very bad habits in general, and many of the things you can read is completely WRONG. Actually, the majority of questions about C can be traced to that book, or at least could have. Two prime examples is that it consistently avoids very important stuff, such as error checking functions like scanf and malloc. I have not read every line, but I think it does not even mention how to error check scanf even once. It also uses the function gets which is not only deprecated but completely removed from newer C standards because it is so dangerous. It also says that you can modify a string literal, which is undefined behavior in C.

klutt
  • 30,332
  • 17
  • 55
  • 95