How does the function getop() from "The C Programming Book" work? - c

int getop(char *s) {
int i, c;
while((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if(!isdigit(c) && c != '.') {
return c;
}
i = 0;
if(isdigit(c))
while(isdigit(s[++i] = c = getch()))
;
if(c == '.')
while(isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if(!isdigit(c))
ungetch(c);
return NUMBER;
}
I came across this fucntion while working on an example named "Reverse polish calculator".
we can input numbers for calculator operations via this function, but I'm not getting the working of this function. Like.,
if we enter some input like ---->
12.34 11.34 +
From
while((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
s will contain 1. But from where does the remaining input goes inside s ?
I've gone through this function well and I came to know it's working but then I want to know the deep working, like the complete flow.
Any help is highly appriciated.
edit:-
After testing various inputs I came to the conclusion that what is the need for getch() and ungetch(), I mean yeah they are there to unread character that is not needed but than look at the test cases.,
int getop(char *s) {
int i, c;
while((s[0] = c = getchar()) == ' ' || c == '\t')
;
s[1] = '\0';
if(!isdigit(c) && c != '.') {
return c;
}
i = 0;
if(isdigit(c))
while(isdigit(s[++i] = c = getchar()))
;
if(c == '.')
while(isdigit(s[++i] = c = getchar()))
;
s[i] = '\0';
/* if(!isdigit(c))
ungetch(c);*/
return NUMBER;
}
Here I replaced getch() with getchar() and it still accepted the input
12a 12 -
and the output was absolutely correct and it unread 'a' character as well
144
and so was the case when I was using getch() ?

Lets break it down piece by piece:
while((s[0] = c = getch()) == ' ' || c == '\t') skips all spaces and tabulation from the beginning fo the string. At the end of this loop s[0] contains the first char which is not either of the two mentioned before.
Now if c is something other than . or a digit we simply return that.
if(!isdigit(c) && c != '.') {
return c;
}
If c is not a digit we are done and it's quite right to set s[1] to null!
Otherwise, if c is a digit we read as much digit chars as we can overwriting s from position 1. s[1] was '\0' but it does not matter because s[++i] would be s[1] at the very first iteration of
i = 0;
if(isdigit(c))
while(isdigit(s[++i] = c = getch()))
so s is kept in a consistent status.

Related

Clarification in getop()

In Dennis Ritchie's "C programming Language" book,
In getop func,
he states that s[1]='\0'
why does he end the array on index 1? What's the significance and need?
In later part he does uses other parts of the array..
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;
}
Because the function might return before the remaining input is read, and then s needs to be a complete (and terminated) string.
The zero char will be overwritten later, unless the test if (!isdigit(c) && c != '.') is true so that the function returns early. Some coding guidelines would discourage returns from the middle of a function, btw.
Only when the function returns early is the s[1] = '\0' relevant. Therefore, one could have coded
int getop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
if (!isdigit(c) && c != '.')
{
s[1] = '\0'; /* Do this only when we return here */
return c; /* not a number */
}
i = 0;
if (isdigit(c)) /* collect integer part */
/* ... */
It is terse code. These guys knew their language and algorithms. But the snippet lacks error handling and thus depends on correct input (invalid input may let s be empty or let s overflow).
That is not untypical. Processing of valid data is often straightforward and short; handling all the contingencies makes the code convoluted and baroque.
s[] might be only a numeric operand like "+","-" etc.
In this case s[1] have to be '\0' to let s be a proper string.

How can I get the numbers from tupple?

I want to get an input from the user like (4,5).But I just want to get the integer values.(4 and 5) I wrote a code for this.But it did not work.How can I fix the problem.
int x, y;
int c;
c = getchar();
while (c != EOF) {
while (c != '(' && c != EOF) {
c = getchar();
}
while (c != ',' && c != EOF) {
c = getchar();
}
x = c;
while (c != ',' && c != EOF) {
c = getchar();
}
while (c != ')' && c != EOF) {
c = getchar();
}
y = c;
}
I get input(1,4). outputs is x= 44 and y= 45 ?
After you find the opening parenthesis, read characters while they are digits (see e.g. isdigit), and create your number.
When the character is not a digit, make sure it's a comma. If it is you read the next number the same as above. Finally make sure you got the closing parenthesis.
The above assumes that there are no whitespace between the parentheses and the numbers, or between the numbers or the comma. Those can be handled by looping and isspace.
As for converting digits to numbers, assuming your system is using ASCII encoding (which is standard on modern PC-like systems) then it's easy since you just subtract '0' from the character to get the digit (see the linked ASCII table to help you understand why). Store the value in a variable, initialized to zero, and multiply by ten as needed.
Depending on the parser you're writing, you could do the handling and recognition of tuples in the parser instead of the lexer. That would make it more flexible.
follow your code flow x value will be character comma.
int c ,x ,y;
c = getchar();
while (c != EOF) {
while (c != '(' && c != EOF) {
c = getchar();
}
c = getchar();
x = c;
while (c != ',' && c != EOF) {
c = getchar();
}
/*x = c;
while (c != ',' && c != EOF) {
c = getchar();
}
*/
c = getchar();
y = c;
while (c != ')' && c != EOF) {
c = getchar();
}
//y = c;
printf("x = %c ,y = %c ",x,y);
}

How does `ungetch` work?

I searched everywhere, but I cannot find the answer to this! I'm working on the exercise from the K&R C books with a function they call getop. When it peeks at the next character from the input and sees that it isn't a digit, where does the character get stored when unget is called? I can compile and run the code so I know it works, I just want to know where the character has been stored.
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;
}
It doesn't really peek, it reads the char into the stream's buffer. On an unbuffered stream, or when you've read nothing after opening the file or seeking, ungetc() isn't guaranteed to work.

C: segmentation fault

I've created this function to read a word. I got segmentation fault and I can't find the problem. Here's what I've done.
void LeeCaracter(FILE * fp, char * s)
{
char c;
int i = 0;
c = fgetc(fp);
while(c==' ' || c=='\t' || c=='\n')
c = fgetc(fp);
while(c!=' ' && c!='\n')
{
s[i] = c;
i++;
c = fgetc(fp);
}
s[i] = '\0';
}
s is a pointer parameter, as I have to use it later. Is it correct to write it just with one *? Thanks for your help!
*And what about if I wanted to know the character that follows the word(' ' or '\n')? I added this after the while loop:
"printf("%c",c);"
but it doesn't print anything. Any ideas?
Consider:
while(c==' ' || c=='\t' || c=='\n')
c = fgetc(fp);
So, at this point, two things that c is not are ' ' and '\n'. Then:
while(c!=' ' && c!='\n')
{
s[i] = c;
i++;
}
Since the value of c does not change in the loop, the while condition is always true. Meaning that pretty quickly, s[i] will go out of bounds. You need to check against the length of s, probably by getting that passed in as a parameter (not to mention, rethink your algorithm a bit -- probably you want to fgetc more inside the loop).
You have to make sure that the 's' has enough space for containing a word with maximum characters in the input file. Then you need to make sure that you check for 'End Of File'. Here is a working version. I hope it works for you as well.
#include <stdio.h>
void LeeCaracter(FILE * fp, char * s)
{
char c;
int i = 0;
c = fgetc(fp);
if (feof(fp)) return;
while (c == ' ' || c == '\t' || c == '\n')
c = fgetc(fp);
while (!feof(fp) && (c != ' ' && c != '\n')) {
s[i++] = c;
c = fgetc(fp);
}
s[i] = '\0';
printf("%s\n", s);
}
int main(void)
{
char s[128]; /* assuming no word is larger than this size */
FILE *fp = fopen("/usr/share/dict/words", "r");
while (!feof(fp)) {
LeeCaracter(fp, s);
}
return 0;
}

Confusion about calculator program

int getop(char s[])
{
int i = 0, c, next;
/* Skip whitespace */
while((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
/* Not a number but may contain a unary minus. */
if(!isdigit(c) && enter code herec != '.' && c != '-')
return c;
if(c == '-')
{
next = getch();
if(!isdigit(next) && next != '.')
return c;
c = next;
}
else
c = getch();
while(isdigit(s[++i] = c)) //HERE
c = getch();
if(c == '.') /* Collect fraction part. */
while(isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if(c != EOF)
ungetch(c);
return NUMBER;
};
what if there is no blank space or tab than what value will s[0] will initialize .......& what is the use of s[1]='\0'
what if there is no blank space or tab than what value will s[0] will intialize
The following loop will continue executing until getch() returns a character that's neither a space nor a tab:
while((s[0] = c = getch()) == ' ' || c == '\t')
;
what is the use of s[1]='\0'
It converts s into a C string of length 1, the only character of which has been read by getch(). The '\0' is the required NUL-terminator.
If there is no space or tab, you're stuck with an infinite loop.
s[1]='\0' is a way of marking the end so functions like strlen()
know when to stop reading through c strings. it's called "Null-Terminating" a string: http://chortle.ccsu.edu/assemblytutorial/Chapter-20/ass20_2.html
while((s[0] = c = getch()) == ' ' || c == '\t')
Read character until its not a tab or space.
s[1] = '\0';
Convert char array s to a proper string in C format (all strings in c must be terminated with a null byte, which is represented by '\0'.

Resources