"As written, getint treats a + or - not followed by a digit as a valid representation of zero. Fix it to push such a character back on the input."
Ok, well this is the original version:
int getint2(int *pn)
{
int c, sign;
while(isspace(c=getch()))
;
if(!isdigit(c) && c!= EOF && c!= '+' && c!= '-') {
ungetch(c);
return 0;
}
sign = (c == '-') ? -1 : 1;
if(c == '+' || c == '-') {
c = getch();
}
for(*pn = 0; isdigit(c); c = getch())
*pn = 10 * *pn + (c - '0');
*pn *= sign;
ungetch(c);
return c;
}
And i edited this way:
int getint2(int *pn)
{
int c, sign;
while(isspace(c=getch()))
;
if(!isdigit(c) && c!= EOF && c!= '+' && c!= '-') {
ungetch(c);
return 0;
}
sign = (c == '-') ? -1 : 1;
if(c == '+' || c == '-') {
c = getch();
if(!isdigit(c)) {
ungetch(c);
return 0;
}
}
for(*pn = 0; isdigit(c); c = getch())
*pn = 10 * *pn + (c - '0');
*pn *= sign;
ungetch(c);
return c;
}
So im not sure what did the author want. Should i unget the +/- aswell, or just the character after it? Should i return 0 in case there's no digits after +/- or -1?
I also have a question about getch and ungetch functions:
since EOF on my sistem is -1, this is how i wrote getch and ungetch:
int buf = EOF;
int getch()
{
int temp;
if(buf == -2)
return EOF;
if(buf == EOF)
temp = getchar();
else
{
temp = buf;
buf = EOF;
}
return temp;
}
void ungetch(int c)
{
if(c == EOF)
buf = -2;
buf = c;
}
So i was told by some people that EOF can be -2. What should i do to avoid this sort of 'problem'.
To answer your second question, about EOF and -2, I can think of a couple of solutions:
you could have an array of characters in the "buffer", and an "index" in the position (i.e., have a rudimentary stack). I believe that arrays are introduced in K&R by that point. That will give you flexibility in that you can ungetch() more than one character. An added advantage of this scheme is that your code in part 1, where you might need to ungetch() twice, becomes easier as well.
you could store the 'state' in another int variable, such as int buffered = 0;, and then getch() would return buf only when buffered == 1. ungetch() sets buffered = 1, getch() sets buffered = 0:
static int buf;
static int buffered = 0;
int getch()
{
if (buffered) {
buffered = 0;
return buf;
}
return getchar();
}
void ungetch(int c)
{
buffered = 1;
buf = c;
}
Related
I'm writing a function that can read integers expressed in hexadecimal.
This is my code. The main function should not be modified.
#include <stdio.h>
#include <ctype.h>
#define SIZE 100
main()
{
int array[SIZE], n;
int gethex(int*);
int i, sum;
for (n = 0; n < SIZE && gethex(&array[n]) != EOF; n++)
;
sum = 0;
for (i = 0; i < n; i++)
sum += array[i];
printf("The sum is %d\n", sum);
}
int gethex(int *pn)
{
int c, sign;
c = getchar();
while (isspace(c))
;
if (!isxdigit(c) && c != EOF && c != '+' && c != '-') {
ungetc(c, stdin);
return 0;
}
sign = (c == '-') ? -1 : 1;
if (c == '+' || c == '-')
c = getchar();
for (*pn = 0; isxdigit (c); c = getchar()) {
if (c >= '0' && c <= '9') {
*pn = 16 * *pn + (c - '0');
}
else if (c >= 'a' && c <= 'f') {
*pn = 16 * *pn + (c - 'a' + 10);
}
else if (c >= 'A' && c <= 'F') {
*pn = 16 * *pn + (c - 'A' + 10);
}
}
*pn *= sign;
if (c == EOF) {
*pn = c;
return c;
}
}
The result value should come out like this.
-FFec
10000
^Z
The sum is 20
However, my code outputs
The sum is 1717986860
What's wrong?
At least these problems:
Warnings not fully enabled
Warnings are not fully enabled, losing OP and others valuable time. #Jens
End of gethex() lacks a return value.
warning: control reaches end of non-void function [-Wreturn-type]
Infinite loop
Once a '\n is read, loop never ends. #Weather Vane
while (isspace(c))
;
Non hex input also set up an infinite loop with ungetc(c, stdin); as that same c will repeatedly get read on the next function call.
if (!isxdigit(c) && c != EOF && c != '+' && c != '-') {
ungetc(c, stdin);
return 0;
}
int overflow possible
Code like *pn = 16 * *pn + (c - '0'); and *pn *= sign; can lead to int overflow, which is undefined behavior (UB). Robust code would prevent that.
Some untested code to fix these issues.
// Return 0 if non-numeric input detected.
// Return 2 when input out of int range
// Return 1 on success
// Return EOF when input is only white-space, sign or none
int gethex(int *pn) {
int ch;
// Consume leading white-space
do {
ch = getchar();
} while (isspace(ch));
// Get sign, if any
int sign = ch; // remember 1st
if (ch == '-' || ch != '+') {
ch = getchar();
}
if (ch == EOF) {
return EOF;
}
if (!isxdigit(ch)) {
// Consume non-numeric input
return 0;
// Perhaps better to return EOF here to stop calling code.
}
int overflow = 0;
int sum = 0;
while (isxdigit(ch)) {
int digit = 0;
if (isdigit(ch)) {
digit = ch - '0';
} else if (isupper(ch)) {
digit = ch - 'A' + 10;
} else if (islower(ch)) {
digit = ch - 'a' + 10;
}
if (sum <= INT_MIN / 16
&& (sum < INT_MIN / 16 || digit > -(INT_MIN % 16))) {
overflow = 1;
sum = INT_MIN;
} else {
// Accumulate as a negative number as there are more
// negative vales than positive ones.
// This prevents bad code like -INT_MIN later.
sum = sum * 16 - digit;
}
}
if (sign != '-') {
if (sum < -INT_MAX) {
overflow = 1;
sum = INT_MAX;
} else {
sum = -sum;
}
}
*pn = sum;
ungetc(ch, stdin);
return overflow ? 2 : 1;
}
I just finished exercise 6.1 in the book The C Programming Language, Second Edition (by K&R), here is the exercise problem:
Our version of getword does not properly handle underscores, string constants,
comments, or preprocessor control lines. Write a better version.
Here is the getword function in the book:
int getword(char *word, int lim)
{
int c;
char *w = word;
while (isspace(c = getch()))
;
if (c != EOF)
*w++ = c;
if (!isalpha(c)) {
*w = '\0';
return c;
}
for ( ; --lim > 0; w++)
if (!isalnum(*w = getch())) {
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
Function calling in the main():
while (getword(word, MAXWORD) != EOF) {...}
Here is my getword function:
int getword(char *word, int lim)
{
int c, c1;
char *w = word;
while (isspace(c = getch()))
;
if (c != EOF)
*w++ = c;
else
return EOF;
// handle comments: /* */
if ( c == '/') {
if ((c1 = getch()) == '*') {
keytab[31].count++;
int ok = 1;
while (ok) {
// skip characters in comment lines
while ((c = getch()) != EOF || c != '*')
;
if (c == EOF)
return EOF;
if ((c = getch()) == '/')
ok = 0;
}
return COMMENT;
}
// handle comments : //
else if (c1 == '/') {
keytab[32].count++;
while ((c = getch()) != EOF || c != '\n')
;
if (c == EOF)
return EOF;
return COMMENT;
}
else
return c;
}
// handle preprocessor control lines, start with '#'
if (c == '#') {
keytab[34].count++;
while ((c = getch()) != EOF || c != '\n')
;
if (c == EOF)
return EOF;
return '#';
}
// handle string constants, " "
else if (c == '\"') {
while ((c1 = getch()) != EOF || c1 != '\"')
;
if (c1 == EOF)
return EOF;
keytab[33].count++;
return CONSTANT;
}
else if (c != '_' && !isalpha(c)) {
while ((c = getch()) != EOF && !isspace(c))
;
if (c == EOF)
return EOF;
return NOT;
}
// c is '_' or letter , scan characters until EOF or space, or punctuation
// to get a complete word
for ( ; --lim > 0; w++)
if ((*w = getch()) == EOF || isspace(*w) || ispunct(*w)) {
ungetch(*w);
break;
}
*w = '\0';
return WORD;
}
If I don't enter ", // , /* or #, the code will run normally and I can stop input by enterint ctrl + d. But once I enter one of the above characters, the program can't read EOF, thus I can't stop the input. I debug the program by gdb but still don't get it. So what happens?
&& versus ||
while ((c = getch()) != EOF || c != '*')
Above is always true.
Perhaps
while ((c = getch()) != EOF && c != '*')
I am reading in a string character by character then for each word found(separated by space) counting the length of each word, and finally printing all that information to the screen.
Sample run: trickier to master
n=8, s=[trickier]
n=2, s=[to]
n=6, s=[master]
n=0, s=[]
n=-1, s=[]
This is correct what I get is this:
n=0, s=[]
n=0, s=[]
n=8, s=[trickier]
n=2, s=[to]
n=0, s=[]
n=6, s=[master]
n=0, s=[]
n=-1, s=[]
The problem is the leading spaces in the string I have looked at a lot of examples of how to trim the leading spaces, but I couldn't get anything to work with my current source code.
Code:
#include "getword.h"
int getword(char *w) {
int iochar;
int index = 0;
int numberofchars = 0;
if (numberofchars == 0 && iochar == '\n') {
w[index] = '\0';
iochar = EOF;
return 0;
}
if (iochar == EOF && numberofchars == 0) {
w[index] = '\0';
return -1;
}
while ((iochar = getchar()) != EOF) {
if (iochar != ' ' && iochar != '\n') {
w[index] = iochar;
index++;
numberofchars++;
} else {
w[index] = '\0';
if (strcmp(w, "done") == 0) {
return -1;
} else {
return numberofchars;
}
}
} //after while loop
} // end of function
int main() {
int c;
char s[STORAGE];
for (;;) {
(void)printf("n=%d, s=[%s]\n", c = getword(s), s);
if (c == -1)
break;
}
}
The code is way too complicated, with some useless and bogus tests producing undefined behavior:
testing iochar in getword() before you even read it with getchar() makes no sense.
combining reading, testing and writing the words in a single printf() call is bogus too: you should instead read, then test, then output if not done.
Here is a simplified version:
#include <stdio.h>
#include <string.h>
#define STORAGE 50
// read a word into an array of size `size`.
// return the number of characters read.
int getword(char *w, size_t size) {
int c;
size_t i = 0;
while (i + 1 < size && (c = getchar()) != EOF) {
if (c == ' ' || c == '\t' || c == '\n') {
if (i == 0)
continue; // ignore leading spaces
else
break; // stop on white space following the word.
}
w[i++] = c;
}
w[i] = '\0';
return i;
}
int main() {
char s[STORAGE];
int len;
while ((len = getword(s, sizeof s)) != 0) {
if (!strcmp(s, "done"))
break;
printf("n=%d, s=[%s]\n", len, s);
}
return 0;
}
I tried to make the code produce desired output, but this is all I could do. There were some bugs which I thought and so I fixed them. See the comments in the code for more details. Hope that solves the problem.
int getword(char * w) {
int iochar = 0;
int index = 0;
int numberofchars = 0;
// I really don't know why those if conditions were required
// Thought they were useless so removed them
while ((iochar = getchar()) != EOF) {
if (iochar != ' ' && iochar != '\n') {
w[index++] = iochar; // slight change here
numberofchars++;
} else {
w[index] = '\0';
// I don't know what this condition is supposed to mean
// so I ignored it
if (strcmp(w, "done") == 0) {
return -1;
} else {
return numberofchars;
}
}
} //after while loop
// Since EOF is encountered, no more characters to read
// So terminate the string with '\0'
w[index] = '\0';
// Here after the loop you should check if some characters were read, but not
// handled. If there are any, return them because that's what you last read
// before EOF was encountered
return (numberofchars > 0 ? numberofchars : -1);
} // end of function
int main()
{
int c;
char s[STORAGE];
for (;;) {
// Put the if condition before printing because if -1 is returned
// it doesn't make sense to print the string at all
c = getword(s);
if (c == -1) break;
printf("n=%d, s=[%s]\n", c, s);
}
}
Here's where I tested this: http://ideone.com/bmfaA3
I can't find the error.
Visual Studio doesn't show error code.
And when I run it, it just goes into infinite loop.
It looks fine to me and looks it is supposed to work.
Please help me to find what I did wrong.
#define BUFSIZE 100
int getch2(char buf[], int bufp)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
int getop(char s[])
{
int c, i;
static char buf[BUFSIZE];
static int bufp = 0;
while ((s[0] = c = getch2(buf, bufp)) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c;
i = 0;
if (isdigit(c))
while (isdigit(s[++i] = c = getch2(buf, bufp)))
;
if (c == '.')
while (isdigit(s[++i] = c = getch2(buf, bufp)))
;
s[i] = '\0';
if (c != EOF)
buf[bufp++] = c;
return NUMBER;
}
I just solved the exercise 4-10 - I replaced (unget/get)char with getline, but I can't add support for negative numbers. The reasoning is simple, if a char is a '-' and the character next to it is a digit or a decimal point we have a negative number. I don't know what I did wrong, first time i tryed something like this:
if(c != '-' && isdigit(line[lp])) {
return c;
}
If we have a negative number, line 2 shouldn't be executed, and the array s will have as the first element a '-'. However, I get an infinite loop and i can't find the problem.
This is the most relevant piece of code for this problem(especially the 4th if statement in getop).
#define MAXLINE 100
char line[MAXLINE];
int lp = 0;
int lineLength = 0;
int getline(char s[], int lim) {
int i, c;
i = 0;
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
s[i++] = c;
if(c == '\n')
s[i++] = c;
s[i] = '\0';
return i;
}
int getop(char s[]) {
if(lp == lineLength) {
lineLength = getline(line, MAXLINE);
lp = 0;
}
if(lineLength == 0)
return EOF;
char c;
int i;
printf("the execution is here\n");
while((s[0] = c = line[lp++]) == ' ' || c == '\t')
/* skip tabs an white spaces */;
s[1] = '\0';
if(!isdigit(c) && c != '.' && c != '-')
return c;
if(c == '-') {
if(isdigit(line[lp]) || line[lp] == '.')
/* nothing */;
else
return c;
}
i = 0;
if(isdigit(c))
while(isdigit((s[++i] = c = line[lp++])))
;
if(c == '.')
while(isdigit((s[++i] = c = line[lp++])))
;
lp--;
return NUMBER;
}
You need to advance lp when you find a '-'. The if's at the bottom of the function don't look for '-'. You'll notice other digits (and the decimal point) advances lp before the function returns, so you need to copy that '-' sign to s[] (or store it in global flag) and increment lp, otherwise it just processes the same character over and over.
Stepping through the code in a debugger would help you see the problem as well, if you look at what lp does for positive numbers vs. negative numbers.