2

Please help me in understanding the below code. The function get_digit takes a character argument by address. I am unable to get what scanf("%1[0123456789]", ch) does here.

If I give 1234 on the terminal then it takes only the first digit. Same is if I give 2345 it takes 2. I have never came across such usage of scanf. Please help me in understanding this feature.

int get_digit ( char *ch )
{
    int rc;

    printf ( "Enter a single digit: " );
    fflush ( stdout );

    if ( rc = scanf ( "%1[0123456789]", ch ) == 1 ) {
        jsw_flush();
    }
    return rc;
}

void jsw_flush ( void )
{
    int ch;
    do
        ch = getchar();
    while ( ch != '\n' && ch != EOF );
    clearerr ( stdin );
}

void fill_table ( char table[] )
{
    char ch;
    while ( get_digit ( &ch ) ) {
        unsigned i =  ch - '0';
        if ( table[i] != 0 ) {
            printf ( "That index has been filled\n" );
        }
        else {
            table[i] = ch;
        }
    }
}

void show_table ( const char table[], size_t size )
{
    size_t i;
    for ( i = 0; i < size; i++ ) {
        printf ( "%c\n", table[i] != 0 ? table[i] : '~' );
    }
}
Mat
  • 202,337
  • 40
  • 393
  • 406
Kundan Kumar
  • 1,974
  • 7
  • 32
  • 54

1 Answers1

5

scanf ( "%1[0123456789]", ch ) scans 1 character (%1) which is a decimal digit ([0123456789]) int the characer pointed to by ch.

The number immediately following the % is the field width, how many characters (maximally) to scan. The characters inside the square brackets are the characters scanf will accept. The scan ends when a character not listed is encountered.

An extremely simple example to scan two-digit numbers:

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

int main(void) {
    char chs[2] = {0};   // space for two digits, zero-initialized
    unsigned u = 0, i;
    if (scanf("%2[0123456789]",&chs[0]) == 1) {
        // parse the number
        for(i = 0; i < 2 && chs[i]; ++i) {
            u = 10*u + chs[i] - '0';
        }
        printf("Got %u\n",u);
    } else {
        puts("Scan failed.");
    }
    return EXIT_SUCCESS;
}

Of course, instead of parsing ourselves, we could make the character array one longer than we expect digits (zero-initialise!, scanf doesn't add a 0-terminator with that format) and leave the parsing to strtoul(chs,NULL,10).

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • @Daniel...Thanks !!! I have never used this form of scanf...Here if I am giving 1234 as input then it takes only 1. So this is correct. Also, what will happen if I make it %2, will it scan 2 characters??And what is the jsw_flush function doing here. scanf is scanning 1 and flush is flushing the remaining characters on the terminal (if I give a printf inside flush). Please clarify my doubts regarding this. – Kundan Kumar Apr 20 '12 at 08:02
  • @Kos standard C (C99, 7.19.6.2p12) – ouah Apr 20 '12 at 08:05
  • 1
    @KundanKumar Yes, with `%2[01...9]` it would scan two digits. The `jsw_flush` clears the input stream, it removes any excess characters typed in and the newline (or finds the end of input before reaching the newline) usually required to send any input to the program (stdin is typically line-buffered) and then sets it to error-free state so stdin can be used for more input later. – Daniel Fischer Apr 20 '12 at 08:08
  • @Daniel..one last doubt.please check the updated code. Even on doing %2 I do not get any change in the output. I was expecting that if I enter 1234 then scanf will scan 2 digits "12" and flush the rest. Here flushing is ok its flushing only 3, 4 and the return key which i confirmed by putting traces. But in table[i] its filling only the first digit.I am confused here. – Kundan Kumar Apr 20 '12 at 08:48
  • 1
    Not sure I understand. You still have only `%1` in the code. If you just change that to `%2`, you have undefined behaviour because you pass it a pointer to a memory block of only one byte (`&ch`). `ch` can only hold one character. Scanning two characters with that address will overwrite something that it shouldn't. To scan `n` characters, you need to pass a pointer to a memory block of at least `n` bytes. – Daniel Fischer Apr 20 '12 at 08:49
  • So in the case above if I want to modify it so that it should scan 2 characters and "unsigned i" in fill_table should take the value of first two characters(rite now its taking only the first character as you said that we are passing pointer to memory block of 1 byte only) entered and flush the rest. what modification is needed in this case...I am really confused here. – Kundan Kumar Apr 20 '12 at 08:55
  • @KundanKumar I've added an example for a two-digit scan. – Daniel Fischer Apr 20 '12 at 09:25