6

I'm studying K&R book. Currently i'm reading function getop() at p.78. I do understand the code but i need clarifications about 2 things.

The code of getop() is as follows:

int getch(void);
void ungetch(int);

/* getop: get next character or numeric operand */
int getop(char s[])
{
    int i, c;
    while ((s[0] = c = getch()) == ' ' || c == '\t')
        ;
    s[1] = '\0';

    if (!isdigit(c) && c != '.')
        return c; /* not a number */

    i = 0;
    if (isdigit(c)) /* collect integer part */
        while (isdigit(s[++i] = c = getch()))
            ;
    if (c == '.') /* collect fraction part */
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0';

    if (c != EOF)
        ungetch(c);

    return NUMBER;
}

My question is about: s[0] in:

 while ((s[0] = c = getch()) == ' ' || c == '\t')

The idea behind the while loop is to skip spaces and horizontal tab, so why are we saving 'c' in s[0]? Why the authors didn't simply write:

while (c= getch() == ' ' || c == '\t')

We are not going to use spaces and tabs later on, why do we need to save c in s[0] for? What is the need for s[0] here?

My second question is about:

s[1] = '\0';

Why are we assigning '\0' (end of string) to s[1] here?

I have read some of the previous answers posted on stackoverflow.com about it but i'm not totally convinced!

The accepted answer about the above question is: "Because the function might return before the remaining input is read, and then s needs to be a complete (and terminated) string."

Ok. But what if input has one white space at the beginning and followed by an operand or operator? In this case, s[1] = '\0' will close the string too early? isn't it?

Lundin
  • 195,001
  • 40
  • 254
  • 396
Hussein Barada
  • 117
  • 1
  • 11

4 Answers4

6

In answer to your first question, the assignment to s[0] in this case is a convenient coding shortcut. The value of c is copied to s[0] for every character read by getch(), regardless of whether it will be used or discarded. If it is to be discarded, no big deal; it will be overwritten on the next iteration of the while() loop. If it is to be used, then it has already been copied into its necessary location in the destination array s[].

In answer to your second question,

But what if input has one white space at the beginning and followed by an operand or operator?

Note that the previous while() loop prevents white space characters (spaces and tabs) from appearing in s[0] after exit from the loop. Therefore, after execution of

s[1] = '\0';

the s[] string will consist of a single character that is neither a space nor a tab, followed by a string terminator.

In the next statement

if (!isdigit(c) && c != '.')
    return c; /* not a number */

the function will return if the character is anything but a digit or a decimal point. This is why it was necessary to terminate the string.

sifferman
  • 2,955
  • 2
  • 27
  • 37
  • 1
    Good explanation, although I would remove positive adjectives such as "convenient", as that suggests that this messy K&R code follows some sort of good C coding practice, while it is actually filled to the brim with the very opposite. – Lundin Oct 23 '15 at 11:44
  • @Lundin: I agree completely that the K&R code in question is terrible from a "good coding practice" standpoint, but the code illustrates certain novelties of the C language that are important for novices to learn. From the standpoint of algorithm design, and I believe from the perspective of the original author of the code (long before the "good coding practices" of today were conceived), the assignment to s[0] at this point in the code is a convenience, since it won't need to be performed later. – sifferman Oct 23 '15 at 22:09
1

But what if input has one white space at the beginning and followed by an operand or operator? In this case, s[1] = '\0' will close the string too early? isn't it?

Nope,

i = 0;
if (isdigit(c)) /* collect integer part */
    while (isdigit(s[++i] = c = getch()))

This makes sure, that if there is something to be read, it will get overwritten on \0, as i=0 and s[++i] would mean, storing in s[1], which contains the \0

Haris
  • 12,120
  • 6
  • 43
  • 70
0

for your first question about: s[0] in:

while ((s[0] = c = getch()) == ' ' || c == '\t')

because the saving 'c' in s[0] help to storing first number in advanced so that we can start our next code from simply i equal to 1.

i = 0;
if (isdigit(c)) /* collect integer part */
    while (isdigit(s[++i] = c = getch()))

the above code is used for storing next string character which is start from index i = 1

About your second question :

we can not do

s[0] = '\0';

because at that time we already stored first number in string at s[0]

see

(s[0] = c = getch())
0

The answers given here are already good, though i would like to add one more point on the 2nd question. "Ok. But what if input has one white space at the beginning and followed by an operand or operator? In this case, s[1] = '\0' will close the string too early? isn't it?" In this case we do not care about the string at all(it would be overwritten anyway if a number is encountered) because the string is used only if a decimal number is encountered , rest of the characters such as '+' or '-' or '\n' are directly returned.
rodeo
  • 1