Need help in understanding order of evaluation of expression in C

I have troubles understanding how expression is evaluated in the below code. I do not understand how code works here

while (isdigit(s[++i] = c = getch()))
        ;

and why do we need

s[1] = '\0';

Full code

#include <ctype.h>
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;    
}

Thank you for any help!

Upvotes: 0

Views: 157

Answers (3)

M.M
M.M

Reputation: 141554

This whole function has a design flaw in that it is not possible to prevent a buffer overflow. It needs to know the size of buffer that s is pointing to, to avoid that.

Anyway, while (isdigit(s[++i] = c = getch())); has the same meaning as:

for (;;)
{
    ++i;
    c = getch();
    s[i] = c;

    if ( ! isdigit(s[i]) )
        break;
} 

There is a reason that c is used instead of just writing s[++i] = getch() .

Here I am assuming that getch (not a standard function) refers to some function which has the same return specification as getchar, i.e. it returns either unsigned char value or EOF.

The int c; is needed so that EOF can be detected. If we did not have c then there is no way of performing the test if ( c != EOF ) at the end of the function. Doing s[i] == EOF would not work because it might mistake a valid character for EOF (or EOF might be out of range of char).

However the code still has a bug. The isdigit function expects the same sort of int value; i.e. in my unpacked version, the final test should be:

if ( !isdigit(c) )

I'd guess that the code author knew about the issue with EOF but either didn't know about isdigit, or assumed his code would only be run on an implementation of it that accepted negative chars.

Writing it more compactly, the line could be replaced with:

i = 1;
// ...

while ( isdigit(c = getch()) )
    s[i++] = c;

Upvotes: 1

lukmac
lukmac

Reputation: 4821

  1. This is the so called chained assignment, see wiki. To understand it, you only need to know that assignment in C has value! Therefore, you can thing of it as equivalent to a one-liner of multiple successive assignment from right to left, as @hacks mentioned.

2.

s[1] = '\0'; 

This is a safety concern and a normal coding practice: you always pad the end of a string with '\0' in C. Since the input argument s[] is supposed to be a char array, thus you need to pad it.

Note that

s[1] will be overwritten if s[0] is a digit or '.',

in which case the 2nd or 3rd while loop will be executed. As before, you also need to pad the s[i] with '\0';

Upvotes: 1

haccks
haccks

Reputation: 106012

= is right associative and therefore isdigit(s[++i] = c = getch()) will be grouped as
isdigit( s[++i] = (c = getch()) ). getch will read and assign a char to c and then c is assigned to s[++i].

Upvotes: 2

Related Questions