0

I researched about this issue all day long and saw all other similar questions but somehow I miss something and my code doesn't work at all. And there is an error I've been noticing; when program sorts numbers in ascending order generally doesn't read first and last number. I just got curious about why this problem occurs and how can I make it work. I guess my code is ridiculous entirely so I don't want to waste your time if you can give some tips it would be enough for me :)

#include <stdio.h>

int main ()
{
    int i,b,c,n;
    int number[200];
    char option;
 do
    {
        printf("Please enter numbers you wanna input: ");
        scanf("%d", &n);
        for(int i=0; i<n; ++i) {
        scanf("%d",&number[i]);
        }
    option = getchar ();
    while(getchar() != '\n');
}
while (option != 'q');


for (i=0; i<n; ++i)
    {
        for (b=i+1; b<n; ++b) 
          {
            if (number[i]>number[b])
            {
                c=number[i]; //i used c as temp.
                number[i]=number[b];
                number[b]=c;
            }
          }
    }
    printf("Sorted order of given numbers is this: \n");
    for (i=0; i<n; ++i)
        printf("%d\n",number[i]);

    return 0; }
  • Just as a side note: It is unsafe to use `scanf` without checking the return value. See this page for further information: [A beginners' guide away from scanf()](http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html) – Andreas Wenzel Dec 06 '20 at 01:27

3 Answers3

2

If you are entering newline characters after numbers, scanf("%d",&number[i]); will leave newline characters inputted in the buffer and it is read by option = getchar ();. Therefore, option cannot correctly get desired user input.

The part

    option = getchar ();
    while(getchar() != '\n');

should be

    scanf(" %c", &option);

Note for the space before %c. This means that whitespace characters (including newline characters9 should be ignored.

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • thank you for your tip it makes sense. my program still doesn't work but I will try to fix it. –  Dec 06 '20 at 00:12
2

As @MikeCAT explained, your immediate problem is with:

do
{
    printf("Please enter numbers you wanna input: ");
    scanf("%d", &n);
    for(int i=0; i<n; ++i) {
        scanf("%d",&number[i]);
    }
    option = getchar ();         /* reads '\n' left in stdin by scanf */
    while(getchar() != '\n');    /* stdin empty -- getchar() blocks waiting for next input */
} while (option != 'q');

The real problem it discloses is scanf() is full of pitfalls and should be avoided for user-input until you understand it well enough to know why it should not be used for user-input. That means knowing each conversion specifier and whether or not it will consume leading whitespace, and further understanding what occurs on a matching failure and what you must do to discard offending input to correct the issue. This doesn't even reach the failure-to-validate EVERY input AND conversion.

These are just the tip of the iceberg as far a scanf() pitfalls go -- which is why all new C programmers are encouraged to use line-oriented input function such as fgets() or POSIX getline() for all user-input. The primary benefit is that line-oriented input functions consume an entire line of user-input each time -- so the potential for leaving characters unread in stdin just waiting to bite you on your next input is eliminated (presuming a reasonable size array is provided for input with fgets())

Using fgets() For User-Input

It doesn't matter what type input function you use, you cannot use any of the correct unless you check the return. Unless you check the return you have no determination whether you user-input succeeded or failed. If you blindly use the variable you think holds the input before you validate the input succeeded -- you are asking for Undefined Behavior. Rule: validate EVERY input AND EVERY conversion...

So how to use fgets()? It is really easy. Simply provide a buffer (character array) large enough to hold the longest expected user input and then MULTIPLY by 4 (or something reasonable). The pointer being DON'T SKIMP ON BUFFER SIZE. You would rather be 10,000 characters too long than 1-character too short. For general user a 1K buffer is fine (1024 bytes). That even protects you if the cat steps on the keyboard.

If you are programming on a micro-controller with limited memory, then reduce the buffer size to 2-times the largest anticipated input. (keep the cats away)

When you need to convert a number from the character array filled, the simplest way is to call sscanf() using the buffer as input (similar to how you are using scanf()). But the benefit here is that it does not matter if the conversion fails, it doesn't leave anything unread in stdin (the read has already taken place, so there is no possibility of the conversion affecting the state of your input stream). If you allow multiple integers to be entered at the same time, then strtol() can handle working from the start to end of your buffer converting values as it goes.

The only caveat when reading string input with a line-oriented function is the function will read and include the '\n' as part of the buffer it fills. You want to remove it if you are storing that value as a string. You can do that with strcspn() which returns the number of characters before any of the characters contained in its reject list. So you simply use "\n" as your reject list and it tells you the number of characters up to the newline. Then you simply use that number to overwrite the '\n' with '\0' trimming the '\n' from the end. For example if you were reading into the buffer called line, then the following is all you need to trim the '\n' from the end of the input:

    char line[1024];
    
    if (fgets (line, sizeof line, stdin))
        line[strcspn (line, "\n")] = 0;

(note: in your case where you are simply converting what is contained in line to a number -- there is no need to trim the '\n' anyway, sscanf() will ignore whtiespace, and a '\n' is whitespace)

So in your case what would reading your input entail? Simply declare a buffer (character array) to hold the user-input and use fgets() handle all user-input from the user. You can re-use the same buffer for all inputs. You can make life easier on yourself by creating a short function that takes the array to fill and a prompt as parameters. Then if the prompt isn't NULL display the prompt and read the user-input -- checking the return.

If fgets() returns NULL it means EOF was reached before any input was received (and it is perfectly valid for the user to cancel input by generating a manual EOF with Ctrl + d, or Ctrl + z on windows). So check the return, if the user canceled input, just handle that gracefully. You could write a getstr() function to help as:

#include <stdio.h>
#include <stdlib.h>     /* for qsort */

#define MAXC 1024       /* if you need a constant, #define one (or more) */
#define MAXI  200
#define PROMPT "No. of intgers to read (max 200): "
...
/* fill array s with string, display prompt if not NULL
 * returns pointer to string on success, NULL otherwise
 */
char *getstr (char *s, const char *prompt)
{
    if (prompt)                                         /* if prompt not NULL, display */
        fputs (prompt, stdout);
    
    if (!fgets (s, MAXC, stdin)) {                      /* read entire line of input */
        puts ("(user canceled input)");                 /* handlie EOF case */
        return NULL;
    }
    
    return s;                   /* convenience return of s for immediate use if needed */
}

int main (void) {
    
    char buf[MAXC];                                     /* array to hold ALL input */
    int number[MAXI] = {0},                             /* initialize all arrays */
        i = 0, n = 0;
    
    while (i == 0 || i != n) {                          /* loop until numbers filled */
        if (!getstr (buf, PROMPT))                      /* validate EVERY user-input */
            return 0;
    ...

Allowing The User To Quit With 'q' (or pressing [Enter] on an empty line)

When filling an array with fgets() that makes responding to any character and taking special actions very, very easy. You have just filled a character array. If you want to check for a special character -- just check the first character (element) in the array! That means all you need to check is the character in buf[0] (or equivalently just *buf -- which is short for *(buf + 0) in pointer notation)

So to allow the user to quit if they entered 'q' (or pressed return on an emply line) all you need is:

        if (buf[0] == 'q' || *buf == '\n')              /* exit on 'q' or empty-line */
            return 0;

You can use this at any point in main(). If you were checking in a function, you would just choose a return that would indicate the user quit (like with a return type of int, just pick an integer, say -1 to indicate the user quit, save 0 for some other failure and 1 to indicate success). But since your logic is in main() simply returning from main() is fine. Since quitting isn't an error, return 0; (equivalent to exit (EXIT_SUCCESS);).

Converting Integer Value From The Array

As mentioned above, after reading with fgets(), if the user didn't quit, then your next job is to convert the digits in the buffer to an integer value. Using sscanf() similar to how you are attempting to use scanf() is fine. The only difference with sscanf() is it takes the buffer holding the digits as its first argument, e.g.

        if (sscanf (buf, "%d", &n) != 1) {              /* validate EVERY conversion */
            fputs ("  error: invalid integer input.\n", stderr);
            continue;
        }

Here if the conversion fails, you handle the error and just continue back to the top of the loop, allowing the user to "try again". If the conversion succeeds, your validation isn't over yet. The input must be a positive value, greater than 0 and less than or equal to 200 -- otherwise with 0 there is nothing to enter, anything less or more than 200 and you would invoke Undefined Behavior attempting to write before or beyond your array bounds. Just add that validation:

        if (n <= 0 || 200 < n) {                        /* validate input in range */
            fprintf (stderr, "  error: out of range, (0 < n <= %d)\n", MAXI);
            continue;
        }

Same as with the last conversion, if the user got it wrong, handle the error and continue; allowing the user to "try again". Once you have a valid number for the number of integers you will save in the array, reading the individual values is exactly the same as reading the number of integer to enter. Here you just loop until the user has entered all the values correctly:

        for (i = 0; i < n;) {                           /* loop reading n integers */
            printf ("number[%2d]: ", i+1);              /* prompt */
            if (!getstr(buf, NULL) ||                   /* read/validate input */
                *buf == 'q' || *buf == '\n')            /* quit on 'q' or empty line */
                return 0;
            if (sscanf (buf, "%d", &number[i]) != 1) {  /* validate conversion */
                fputs ("  error: invalid integer input.\n", stderr);
                continue;
            }
            i += 1;                                     /* only increment on good input */
        }

(note: how the value for i is only incremented if the user provides valid-input)

Sorting In C (qsort)

You are learning C. No matter what you need to sort, C provides qsort() as part of the standard library that will sort any array you need (and is much more fully tested and less error prone than trying to "whip up a sort yourself out of thin air....". qsort() sorts arrays of any type object. The only thing that makes new C programmers eyes roll back in their heads is the need to write a compare function that tells qsort() how to sort your array. (it's really quite simply once your eyes roll back forward)

Every compare function has the same declaration:

int compare (const void *a, const void *b)
{
    /* cast a & b to proper type,
     *    return:  -1  - if a sorts before b
     *              0  - if a & b are equal
     *              1  - if b sorts before a
     */
}

const void *what?? Relax. a and b are just pointers to elements in your array. (qsort() uses a void* type so it can pass any type object to the compare function) Your job in writing the compare function is just to cast them back to the proper type and then write the logic above. In your case a is a pointer to int (e.g. int*), so all you need to do is cast to (int*) and then dereference the pointer to get the integer value, e.g.

/* qsort compare function, sort integers ascending
 * using result of (a > b) - (a < b) prevents overflow
 * use (a < b) - (a > b) for descending.
 */
int cmpint (const void *a, const void *b)
{
    int ia = *(int*)a,      /* a & b are pointers to elements of the array to sort */
        ib = *(int*)b;      /* cast to correct type and dereference to obtain value */
    
    return (ia > ib) - (ia < ib);   /* return difference:  -1 - a sort before b
                                     *                      0 - a and b are equal
                                     *                      1 - b sorts before a
                                     */
}

(note: simply returning ia - ib would work, but it would be subject to integer overflow if ia was a large negative value and ib a large positive value or vice-versa. So you use the difference of two comparison operations to eliminate that chance -- try it, pick two numbers for ia and ib and see how that works out...)

Now if my array is number and I have n elements in it and I named my compare function cmpint how difficult is it to use qsort() to sort the values in the number array??

    qsort (number, n, sizeof *number, cmpint);          /* sort numbers */

(done!)

Putting It Altogether*

If you wrap it all up into your program, you would end up with:

#include <stdio.h>
#include <stdlib.h>     /* for qsort */

#define MAXC 1024       /* if you need a constant, #define one (or more) */
#define MAXI  200
#define PROMPT "No. of intgers to read (max 200): "

/* qsort compare function, sort integers ascending
 * using result of (a > b) - (a < b) prevents overflow
 * use (a < b) - (a > b) for descending.
 */
int cmpint (const void *a, const void *b)
{
    int ia = *(int*)a,      /* a & b are pointers to elements of the array to sort */
        ib = *(int*)b;      /* cast to correct type and dereference to obtain value */
    
    return (ia > ib) - (ia < ib);   /* return difference:  -1 - a sort before b
                                     *                      0 - a and b are equal
                                     *                      1 - b sorts before a
                                     */
}

/* fill array s with string, display prompt if not NULL
 * returns pointer to string on success, NULL otherwise
 */
char *getstr (char *s, const char *prompt)
{
    if (prompt)                                         /* if prompt not NULL, display */
        fputs (prompt, stdout);
    
    if (!fgets (s, MAXC, stdin)) {                      /* read entire line of input */
        puts ("(user canceled input)");                 /* handlie EOF case */
        return NULL;
    }
    
    return s;                   /* convenience return of s for immediate use if needed */
}

int main (void) {
    
    char buf[MAXC];                                     /* array to hold ALL input */
    int number[MAXI] = {0},                             /* initialize all arrays */
        i = 0, n = 0;
    
    while (i == 0 || i != n) {                          /* loop until numbers filled */
        if (!getstr (buf, PROMPT))                      /* validate EVERY user-input */
            return 0;
        if (buf[0] == 'q' || *buf == '\n')              /* exit on 'q' or empty-line */
            return 0;
        if (sscanf (buf, "%d", &n) != 1) {              /* validate EVERY conversion */
            fputs ("  error: invalid integer input.\n", stderr);
            continue;
        }
        if (n <= 0 || 200 < n) {                        /* validate input in range */
            fprintf (stderr, "  error: out of range, (0 < n <= %d)\n", MAXI);
            continue;
        }
        for (i = 0; i < n;) {                           /* loop reading n integers */
            printf ("number[%2d]: ", i+1);              /* prompt */
            if (!getstr(buf, NULL) ||                   /* read/validate input */
                *buf == 'q' || *buf == '\n')            /* quit on 'q' or empty line */
                return 0;
            if (sscanf (buf, "%d", &number[i]) != 1) {  /* validate conversion */
                fputs ("  error: invalid integer input.\n", stderr);
                continue;
            }
            i += 1;                                     /* only increment on good input */
        }
    }
    
    qsort (number, n, sizeof *number, cmpint);          /* sort numbers */
    
    puts ("\nsorted values:");                          /* output results */
    for (i = 0; i < n; i++)
        printf (i ? " %d" : "%d", number[i]);           /* ternary to control space */
    putchar ('\n');                                     /* tidy up with newline */
    
}

Example Use/Output

Used as intended:

$ ./bin/fgets_n_integers+sort
No. of intgers to read (max 200): 10
number[ 1]: 321
number[ 2]: 8
number[ 3]: -1
number[ 4]: 4
number[ 5]: -2
number[ 6]: 0
number[ 7]: -123
number[ 8]: 123
number[ 9]: 6
number[10]: 2

sorted values:
-123 -2 -1 0 2 4 6 8 123 321

Abused with bad input (intentionally) and using 'q' to quit in the middle:

$ ./bin/fgets_n_integers+sort
No. of intgers to read (max 200): bananas
  error: invalid integer input.
No. of intgers to read (max 200): 0
  error: out of range, (0 < n <= 200)
No. of intgers to read (max 200): 201
  error: out of range, (0 < n <= 200)
No. of intgers to read (max 200): 5
number[ 1]: bananas again!!!!!!!!!!!!!!!!!
  error: invalid integer input.
number[ 1]: twenty-one
  error: invalid integer input.
number[ 1]: 21
number[ 2]: 12
number[ 3]: done
  error: invalid integer input.
number[ 3]: really
  error: invalid integer input.
number[ 3]: q

When you write any input routine -- go try and break it! If it fails, figure out why, fix it and try again. When you have tried every bad corner-case you can think of and your input routine continues to work -- you can feel reasonably good about it -- until the user you give it to does something totally screwy and finds a new corner case (fix that too)

The Corner-Case Left For You

What happens if the user wants to enter 1 number in the array? Is there any reason to sort? Go back and look at the code and figure out where you could either prevent the user from entering 1 as a valid input OR, just output what the first number the user enters and skip sorting, etc.. -- up to you.

As always, this ended up way, way longer than I anticipated. But seeing where you were stuck on your program -- there no short way to help you see what you need to do and why without a few extra (dozen) of paragraphs. So there is a lot here. SLOW DOWN, digest it, have a talk with the duck (See How to debug small programs -- don't laugh, it works)

And then, if you still have additional questions, drop a comment below and I'm happy to help further.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • That was super helpful!! Thanks a lot I will study your whole comment. I am really grateful. –  Dec 06 '20 at 17:55
  • Glad to help. There is a lot to learn with C (or any programming language if you really *learn* it), and it is more a journey than a race. Slow down and enjoy the journey. You learn C the same way you would eat a whale -- one byte at a time `:)` – David C. Rankin Dec 06 '20 at 19:41
0
    #include <stdio.h>
    
    int main ()
    {
        int i,b,c,n;
        int number[200];
        char option;
        char cont;
     do
        {
            x:
            printf("\nPlease enter numbers you wanna input: ");
            scanf("%d", &n);
            for(i=0; i<n; i++) {
            scanf("%d",&number[i]);
            }
            option = getchar();
       }
    while (option != 'q');
    
    for (i=0; i<n; i++)
        {
            for (b=i+1; b<n; b++) 
              {
                if (number[i]>number[b])
                {
                    c=number[i]; //i used c as temp.
                    number[i]=number[b];
                    number[b]=c;
                }
              }
        }
    printf("Sorted order of given numbers is this: \n");
    for (i=0; i<n; i++)
        printf("%d\n",number[i]);
    printf("Do you want to continue? Press k : (press any key to turn it off) ");
    cont = getche();
    if(cont == 'k')
        goto x;
    else
        exit(0);
    
    return 0; 
}

I think the error is in the index process of array, for and do-while loop. I made a few small changes. I have added "goto" for convenience. I changed the index values of loop and I fixed do while loop.

msertacc
  • 97
  • 1
  • 8
  • The function `getche` is not part of ISO C, but is a platform-specific extension. This function exists on Microsoft Windows, but [not on Linux](https://stackoverflow.com/questions/7469139/what-is-the-equivalent-to-getch-getche-in-linux). Since the OP did not specify that they are using a specific platform, it does not seem appropriate to use platform-specific extensions. – Andreas Wenzel Dec 06 '20 at 01:33
  • Thanks for telling. It can also be solved with getchar(). I just wanted to use it. – msertacc Dec 06 '20 at 01:43
  • Thanks a lot! It helped me so much. I was really confused so everything went downhill from some point in my code. –  Dec 06 '20 at 17:59