K&R Exercise 4-3 - c

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.

Related

Can't find error on my code for Exercise 4-11. Getop Ungetch

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;
}

C Program, Nothing printing in terminal

I have the following c program that should print our a vertical histogram of the lengths of the words in its input.
#include <stdio.h>
#define MAX_WORD_LENGTH 35 /* maximum word length we will support */
int main(void)
{
int i, j; /* counters */
int c; /* current character in input */
int length; /* length of the current word */
int lengths[MAX_WORD_LENGTH]; /* one for each possible histogram bar */
int overlong_words; /* number of words that were too long */
for (i = 0; i < MAX_WORD_LENGTH; ++i)
lengths[i] = 0;
overlong_words = 0;
while((c = getchar()) != EOF)
if (c == ' ' || c == '\t' || c == '\n')
while ((c = getchar()) && c == ' ' || c == '\t' || c == '\n')
;
else {
length = 1;
while ((c = getchar()) && c != ' ' && c != '\t' && c != '\n')
++length;
if (length < MAX_WORD_LENGTH)
++lengths[length];
else
++overlong_words;
}
printf("Histogram by Word Lengths\n");
printf("=========================\n");
for (i = 0; i < MAX_WORD_LENGTH; ++i) {
if (lengths[i] != 0) {
printf("%2d ", i);
for (j = 0; j < lengths[i]; ++j)
putchar('#');
putchar('\n');
}
}
}
I have this compiled as a.out, at the terminal I do ./a.out, I type in a word and nothing happens. Any help? I am new to C and just trying to learn.
Your program doesn't print anything out until after getchar() returns EOF. That means entering a word and hitting return won't do it. You need to press ^D on a blank line to tell your terminal emulator to close the input stream.
A quick test here seems to show that your program works. You may want to check on the order of operations in your big &&/|| logic - clang gave me some warnings about && within ||.

Get whole number from input using only getchar() and convert to int

For a school assignment i have to make a C program that reads a whole number, that may be preceded by a '+' or '-'. i can only use getchar().
I can convert a single char to an int using int i = ch - '0' but I want to read in multiple chars. besides that I need to check if the input is correct (eq. no not-numerical charachters)
I have this so far:
int main(void) {
int i;
char ch;
printf("voer een getal in: ");
while ((ch = getchar()) != '\n') {
i = ch - '0';
}
printf("het ingevoerde getal is: %d\n", i);
return EXIT_SUCCES;
}
Edit: I get that this isn't really the place for problems like this (I should learn to fix it myself, not get others to fix it for me) but I didn't know what to do anymore. thanks you for guiding me to the right path
This should get you started:
int main(void) {
int i = 0;
int ch;
printf("voer een getal in: ");
while ((ch = getchar()) != '\n') {
if (ch > '9' || ch < '0')
continue; // or print an error and exit
i *= 10;
i += ch - '0';
}
printf("het ingevoerde getal is: %d\n", i);
return EXIT_SUCCES;
}
I'll leave detecting the potential + / - sign as an exercise.
If your school allows you to use shift operators then here is a quick way to get integer from the user and as per their requirements you can show the + or - sign preceding the integer. But remember not to just copy paste it. Understand the code first.
int main(){
int i = 0;
char c;
char sign = 43;
printf("voer een getal in:\n");
c = getchar();
if(c == 45)
sign = 45;
for(; (c<48 || c>57); c = getchar());
for(; c>47 && c<58 ; c = getchar()){
i = (i<<1) + (i<<3) + c - 48;
}
printf("het ingevoerde getal is: %c%d\n",sign, i);
return 0;
}
Reading in multiple characters, and determine whether positive, negative, and value:
int sign = 1;
ch = getchar(); //look at first char (might be a sign indicator)
if((ch == '-') || (ch == '+')) //consume first char if either `-` or `+`
{
if(ch == '-') sign = -1;
}
else //first char non-sign - handle as digit
{
if (ch > '9' || ch < '0')
continue; // or print an error and exit
i *= 10;
i += ch - '0';
}
while ((ch = getchar()) != '\n') //get remaining chars (assume no embedded sign chars)
{
if (ch > '9' || ch < '0')
continue; // or print an error and exit
i *= 10;
i += ch - '0';
}
i *= sign; //apply sign to value
I think this will work.
while ((ch = getchar()) != '\n') {
i = ch - '0' + i * 10;
The problem in your code is that you are overwriting i every time you read a new character. You need to store the digit you read, and then add to it.
You should take care about not number characters :
while((ch=getchar()) != '\n')
if(ch >= '0' && ch <= '9')
i += 10* i + (ch - '0');
For the '-' or '+' at the begining you could store the first getchar(). A solution could be :
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int i = 0;
char ch,first_ch;
printf("voer een getal in: ");
ch = first_ch = getchar();
while (ch != '\n') {
if(ch >= '0' && ch <= '9')
i = 10* i + (ch - '0');
ch = getchar();
}
if(first_ch == '-')
i *= -1;
printf("het ingevoerde getal is: %d\n", i);
return EXIT_SUCCESS;
}

Does this small C program satisfy the K&R exercise?

I'm on to K&R's Exercise 1-18
Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.
This is what I've came up with so far
#include <stdio.h>
#define MAXLINE 1000
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int main () {
int len;
char line[MAXLINE];
while (getline(line, MAXLINE) > 0) {
printf("%s", line);
}
return 0;
}
int getline(char s[], int lim) {
int c, i, lastNonBlankIndex;
lastNonBlankIndex = 0;
for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
if (c != ' ' && c != '\t') {
lastNonBlankIndex = i + 1;
}
s[i] = c;
}
if (i != lastNonBlankIndex) {
i = lastNonBlankIndex;
c = '\n';
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
The second part sounded hard, as I wasn't sure what I should return if the line only has blanks or tabs. After all, if I return 0, it will halt the getline() calling. Would this be where I should set up a #define, such as ALL_BLANKS.
Anyway, to actual main question, is this a correct way to remove trailing blanks and tabs from lines? I ran a few inputs through, and it seemed to work. However, if I copy and paste text with newlines into the CL, it appears all strung together. And when I type a line into the CL and push enter, it automatically prints it. Should I be building an array of lines, and then looping through and printing them when done ?
Your code looks correct, but I think it would be better if you separate the operations of reading a line from stdin and stripping the line of trailing whitespace (decoupling). Then you can use the unmodified getline from the book (code reuse) and won't have the problem of halting on returning 0.
And if you are interested in other solutions, the CLC-wiki has an almost complete list of K&R2 solutions.
#include <stdio.h>
#define MAXLINE 1024
int getline(char s[], int lim);
main()
{
int i, len;
char line[MAXLINE];
while ((len = getline(line, MAXLINE)) > 0) {
i = len - 2;
while (i >= 0 && (line[i] == ' ' || line[i] == '\t'))
--i;
if (i >= 0) {
line[i+1] = '\n';
line[i+2] = '\0';
printf("%s", line);
}
}
return 0;
}
This is the category 1 solution I wrote some time ago. getline is as on page 28 of the book. It might be nicer to put the removal of whitespace in a separate function rstrip, but I leave this as an exercise for the reader.
Your basic design is sound. It is better, as you did, to print a stripped line as soon as you've built it, so that your program only needs to keep one line at a time in memory and not the whole file.
There is a small problem with your code: it doesn't implement the second part of the question (“delete entirely blank line”). That's because you always tack a '\n' at the end of the string. This is easy to fix, but remember that you must return a nonzero value to your caller since a blank line doesn't indicate the end of the file.
getline should return -1 (a negative value in general) if there is an error or if EOF is reached. Then your loop conditional can check that it returns something >= 0 and still allow for 0 length lines.
for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
I almost never include an assignment within a loop conditional. I would rather add 10 lines of code to get around doing that because it's difficult to read. I would especially refrain from using them with complicated conditionals.
int i = 0;
while (i < lim) {
c = getchar();
if (c == EOF || c == '\n') {
break;
}
line[i] = (char)c;
i++;
}
line[i] = '\0'; // Null terminate the string
This code should read in a line for you. I would separate the reading in of the line from the removal of the trailing white space. You could very easily work backwards from the end of the string to remove white spaces at the location where I null terminated the line, since after having read in the line you now know its length. Essentially you grow the string and then you prune it back down after it has finished growing.
This is how i did it.
#include <stdio.h>
#define MAXLINE 1000
#define IN 1
#define OUT 0
int state = OUT;
int getline(char s[], int lim);
void copy(char to[], char from[]);
int main(void)
{
int lenght;
int max = 0;
char line[MAXLINE];
char longest[MAXLINE];
while ((lenght = getline(line, MAXLINE)) > 0)
if (lenght > max)
{
max = lenght;
copy(longest, line);
}
if (max > 0)
printf("\n%s\n", longest);
return 0;
}
int getline(char s[], int lim)
{
int i, c;
for (i = 0; i < lim - 1 && ((c = getchar()) != EOF) && (c != '\n'); i++)
{
if (state == IN && c != ' ' && c != '\t')
{
s[i] = ' ';
i++;
state = OUT;
}
if (s[0] == ' ')
{
s[0] = '\b';
}
s[i] = c;
if (c == ' ' || c == '\t')
{
i--;
state = IN;
}
}
if (c == '\n')
{
s[i] = c;
i++;
}
s[i] = '\0';
return i;
}
void copy(char to[], char from[])
{
int i = 0;
while ((to[i] = from[i]) != '\0')
i++;
}
#include <stdio.h>
#define MAXLINE 1000
size_t getline(char *s,size_t lim)
{
if( fgets(s,lim,stdin) )
{
while( *s && strchr(" \t\n",s[strlen(s)-1]) )
s[strlen(s)-1]=0;
return strlen(s);
}
return 0;
}
main()
{
int len;
char line[MAXLINE];
while (getline(line,sizeof line)) {
printf("%s", line);
}
return 0;
}

getint and getch

"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;
}

Resources