Alright, so I'm doing exercise 8 in K&R second edition. Upon looking up the answer after my attempt at doing the exercise didn't print anything but the newlines (the other ints for tabs and empty spaces remained 0 despite running loops to count - I later found out that I used the wrong character for blank space which is just a blank space but it still neglected to count '\t' correctly), I found this:
#include <stdio.h>
int main(void)
{
int blanks, tabs, newlines;
int c;
int done = 0;
int lastchar = 0;
blanks = 0;
tabs = 0;
newlines = 0;
while(done == 0)
{
c = getchar();
if(c == ' ')
++blanks;
if(c == '\t')
++tabs;
if(c == '\n')
++newlines;
if(c == EOF)
{
if(lastchar != '\n')
{
++newlines;
}
done = 1;
}
lastchar = c;
}
printf("Blanks: %d\nTabs: %d\nLines: %d\n", blanks, tabs, newlines);
return 0;
}
Now this works fine. K&R is interesting in that it uses ideas not taught to you in the actual text, for instance I tried to run my "while" loop with multiple IFs the same way this one does, except my WHILE loop ran only when getchar was != EOF. I want to know why it didn't work that way.
I found that what they did is a much better idea, creating the int done and then assigning it a 1 instead of 0 at the end of the program was a much better idea, but mine still ran somewhat correctly. (sorry I don't have my own original code this time).
Where I am stumped is what is the purpose of main(void) and return 0;? Before starting this book I found criticism on this but readers claimed it was only in the 1st edition. Here I find that the 2nd edition doesn't teach that but then puts it in the solutions text.
Also, what is the purpose of the int "lastchar"? If getchar(c) is the input and lastchar is always defined as 0, then how could lastchar possibly be changed by any input whatsoever to make it meaningful to the program at all by running a loop to count newlines with it? I see that lastchar is defined as 'c' at the end of the program, but how does that pertain to it being called previously?
Sorry if any of my questions are complicated. Please just answer whatever you can and let me know if you need any further clarification. Just to reiterate I'm very curious why the program can't run a while loop using getchar(c) != EOF, with the same IF statements. Rather than using while done == 0. I feel as if it could be a little shorter/concise (definitely can't say simpler) that way.
Where I am stumped is what is the purpose of main(void) and return 0;?
In standard C programs, main(0) should return an int, and 0 indicates successful program completion. One could argue that main should have two parameters -- the command-line argument count and an array of arguments, but if your program doesn't make use of arguments then it isn't necessary.
Also, what is the purpose of the int "lastchar"?
And the end of the while loop, the program stores a copy of the current character in the lastchar variable. As you can see in the EOF-handling code, it makes use of lastchar when determining whether the input text ended in a partial line.
I'm very curious why the program can't run a while loop using getchar(c) != EOF, with the same IF statements.
You could code it that way, but the conditional for the while can appear confusing to someone who doesn't have a lot of experience with C: while ((c = getchar()) != EOF). You would also have to move the if (lastchar != '\n') ++newlines; to just outside of the while loop.
Maybe you should make that change to the program and compare it's output to the original for various types of input (empty file, file ending with a newline, file not ending with a newline). Do both programs show the same output? If not, why? Does the modified version still seem more concise? Which would be easier to make changes to in the future?
Many decisions go into a choice of how to structure a program. Even one as simple as this K&R example.
Related
Exercise 1-9 in The C Programming Language by Denis Ritchie and Brian Kernighan, second edition:
Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank.
Given that I'm using the book as a reference, I know only the C principles which have been discussed in the book up to Exercise 1-9, that is, variable assignments, while- and for-loops, if-statements, symbolic constants, character I/O via getchar() and putchar(), escape sequences and the printf() function. Maybe I'm forgetting something, but that's most of it. (I'm at page 20, where the exercise is at.)
Here's my (not working) code:
#include <stdio.h>
main()
{
int c;
while ((c = getchar()) != EOF) { // As long as EOF is not met, repeat the following…
if(c == ' ') { // If the input character is a blank, proceed (otherwise skip)…
putchar(c); // Output the blank space which was just inputed…
while(c == ' ') { // As long as more spaces keep coming in, don't do anything (proceed when another character comes along)…
;
}
}
else { // When a character other than a blank is inputed, output that character…
putchar(c);
} // Now retest the master while-loop condition (EOF not met) and proceed…
}
}
What I'm getting as a result is a working input-to-output program, that keeps on inputting and stops outputting the moment a blank is typed. (An exception to this is if the blank is removed with a backspace before entering a new line in the console.)
For example, the input abcde\nabcde abcde\nabcde will yield the output abcde, omitting the second and third lines, given that a blank is contained in the former. Here I am obviously using \n to represent an inputted new line (normally using the Enter key).
What have I done wrong, and what could I do to fix this issue? I know there are several working models of this program spread all over the internet, but I'm wondering why this one (which is my creation) in particular doesn't work. Again, do note that my knowledge of C is mostly limited to the first twenty pages of the book whose details are provided below.
Specs:
I'm running Eclipse version 2021-12 (4.22.0) on Debian GNU/Linux 11 (bullseye). I downloaded the pre-compiled Eclipse version from the official Eclipse.org website.
References:
Kernighan, B.W. and Ritchie, D.M. (1988). The C programming language / ANSI C Version. Englewood Cliffs, N.J.: Prentice Hall.
while(c == ' ') is forever loop.
you should try to remember previous character and if prevous character is whitespace and current character is also whitespace, skip it.
I have a few questions on this exercise. Here is the code I'm dealing with:
#include <stdio.h>
int main (void)
{
int c;
int inspace;
inspace = 0;
while((c = getchar()) != EOF)
{
if(c == ' ')
{
if(inspace == 0)
{
inspace = 1;
putchar(c);
}
}
if(c != ' ')
{
inspace = 0;
putchar(c);
}
}
return 0;
}
(Sorry I'm having a lot of trouble comprehending how these programs work because they're so simple and lack description on how they actually work)
First of all, how does putchar(c) not output the same exact data that came in. Despite it checking for a blank or != blank, it still says to output "c" which is just getchar(c) meaning whatever was inputted. I see no code that specifies to delete extra spaces and output just one space. Where does the code specify that that is what must take place? I'm having trouble understanding how getchar/putchar works it seems to me.
Also, what importance does inspace == 1 or 0 have? If inspace is == 1 then it just outputs the characters inputted back out. There's nothing saying that the extra blanks are deleted and inspace isn't defined as anything except 0 or 1, there's nothing defining it as a space so how can it possibly have any real meaning as to what the program is doing?
I'm really confused, where is the code that's replacing the spaces and how does it work? Is there a simpler book I should be learning from that explains the solutions?
First of all, how does putchar(c) not output the same exact data that came in. Despite it checking for a blank or != blank, it still says to output "c" which is just getchar(c) meaning whatever was inputted. I see no code that specifies to delete extra spaces and output just one space. Where does the code specify that that is what must take place? I'm having trouble understanding how getchar/putchar works it seems to me.
You are correct that if putchar is called, it just outputs the input character. The key to this program is that putchar isn't called on every input character. The various if statements control when it is called. At a high level, the program avoids calling putchar on the second, third, fourth, etc., spaces if there are multiple spaces in a row. It's only called on the first space.
Also, what importance does inspace == 1 or 0 have? If inspace is == 1 then it just outputs the characters inputted back out. There's nothing saying that the extra blanks are deleted and inspace isn't defined as anything except 0 or 1, there's nothing defining it as a space so how can it possibly have any real meaning as to what the program is doing?
Don't think of it as spaces being deleted. Think of it as them being omitted. Sometimes putchar is called, sometimes it isn't. Look at the loop and try to figure out what conditions would cause putchar not to be called.
Importantly, look at what happens if you start a loop iteration, inspace == 1, and c == ' '. What happens?
It might help to put together a table showing when putchar is and isn't called.
Is putchar(c) called?
=====================
| c == ' ' | c != ' '
-------------+----------+---------
inspace == 0 | Y | Y
inspace == 1 | N | Y
Think about the logic in this block when there are two ore more consecutive ' ' characters in the input.
if(c == ' ')
{
if(inspace == 0)
{
inspace = 1;
putchar(c);
}
}
When the first space character is encountered, the code enters the nested if block and prints the character.
When the second space character is encountered, the code does not enter the nested if block and the character is not printed.
If you follow this logic, you'll notice that if there are two or more consecutive space characters in the input, only one is printed.
how does putchar(c) not output the same exact data that came in.
When the code reaches putchar(c), it outputs the same exact character that came in. However, the code may not be reaching putchar(c) on some of the iterations.
what importance does inspace == 1 or 0 have?
Once the program sets inspace to 1, it stops printing further space characters, because the code will not reach putchar(c) on second and subsequent iterations of the loop.
inspace is set to 1 after printing the first space in a sequence of one or more spaces. If inspace is set to zero coming into the first conditional, a space would be printed; otherwise, no space would be printed.
Here is a diagram that explains what is happening:
The program starts in the black circle, and proceeds to one of two states, depending on the input character:
If the character is space, the state on the left is entered, when the first space is printed, and the rest of spaces are ignored (i.e. inspace is set to 1)
If the character is non-space, the state on the right is entered, when each character is printed.
Each time a new character is read the program decides if it wants to switch the state, or to remain in the current state.
Note: the diagram is not showing the EOF to save some space. When EOF is reached, the program exits.
The first if block along with the inner one is saying, "If the input is a space and the inspace flag is zero, print it, and also set the flag to one". I.e. print space if it is the first one, and indicate the next one won't be the first. The second block is saying "If the input is not space, print it and reset the previous spaces flag so the next encountered space will be considered the first one.". That's all.
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
char ch;
int count;
while ((ch = getchar()) != '\n')
for (count = 0, count <= (ch - '0'), count++)
printf("%c", ch);
return 0;
}
I haven't really gotten to the logistics of the coding. Just trying something. But as I was writing what seems like a simple for loop for the first time in C, I keep coming across an error on visual studio telling em to put a semicolon after the ) in the for loop statement. Well the problem is, even after I put one there, it keeps telling me I need a semicolon after the ).
I know that sometimes, the error might be somewhere else and it just tells you something totally irrelevant. I haven't even gotten to much coding yet and I can't find any really obvious mistakes.
I tried using brackets to block in the while and the for loops. If I remember correctly, I don't think you need a semicolon after the for statement...
Does anyone know what the problem might be? I thought maybe the project itself got a little faulty, so I opened a new project and wrote the new code in there. Sometimes, that works. I would just copy and paste the same exact code on a new project and it builds with no errors.
A lot of time has passed from I last used C++ but your loop should be
for (count = 0; count <= (ch - '0'); count++)
I believe it's asking you to put the semicolon after count <= (ch - '0'). Note that a for loop syntax is for (;;), where the each part can contain many expressions separated by commas.
Thank you everybody so far for your input and advice!
Additionally:
After testing and toying further, it seems individual calls to FileReader succeed. But calling FileReader multiple times (these might be separate versions of FileReader) causes the issue to occur.
End Add
Hello,
I have a very unusual problem [please read this fully: it's important] (Code::Blocks compiler, Windows Vista Home) [no replicable code] with the C File Reading functions (fread, fgetc). Now, normally, the File Reading functions load up the data correctly to a self-allocating and self-deallocating string (and it's not the string's issue), but this is where it gets bizarre (and where Quantum Physics fits in):
An error catching statement reports that EOF occurred too early (IE inside the comments section at the start of the text file it's loading). Printing out the string [after it's loaded] reports that indeed, it's too short (24 chars) (but it has enough space to fit it [~400] and no allocation issues). The fgetc loop iterator reports it's terminating at just 24 (the file is roughly 300 chars long) with an EOF: This is where it goes whacky:
Temporarily checking Read->_base reports the entire (~300) chars are loaded - no EOF at 24. Perplexed, [given it's an fgetc loop] I added a printf to display each char [as a %d so I could spot the -1 EOF] at every step so I could see what it was doing, and modified it so it was a single char. It loops fine, reaching the ~300 mark instead of 24 - but freezes up randomly moments later. BUT, when I removed printf, it terminated at 24 again and got caught by the error-catching statement.
Summary:
So, basically: I have a bug that is affected by the 'Observer Effect' out of quantum physics: When I try to observe the chars I get from fgetc via printf, the problem (early EOF termination at 24) disappears, but when I stop viewing it, the error-catch statement reports early termination.
The more bizarre thing is, this isn't the first time it's occurred. Fread had a similar problem, and I was unable to figure out why, and replaced it with the fgetc loop.
[Code can't really be supplied as the code base is 5 headers in size].
Snippet:
int X = 0;
int C = 0;
int I = 0;
while(Copy.Array[X] != EOF)
{
//Copy.Array[X] = fgetc(Read);
C = fgetc(Read);
Copy.Array[X] = C;
printf("%d %c\n",C,C); //Remove/add this as necessary
if(C == EOF){break;}
X++;
}
Side-Note: Breaking it down into the simplest format does not reproduce the error.
This is the oldest error in the book, kind of.
You can't use a variable of type char to read characters (!), since the EOF constant doesn't fit.
You need:
int C;
Also, the while condition looks scary, you are incrementing X in the loop, then checking the (new) position, is that properly initialized? You don't show how Copy.Array is set up before starting the loop.
I would suggest removing that altogether, it's very strange code.
In fact, I don't understand why you loop reading single characters at all, why not just use fread() to read as much as you need?
Firstly, unwind's answer is a valid point although I'm not sure whether it explains the issues you are seeing.
Secondly,
printf("%d %c\n",C,C); //Remove/add this as necessary
might be a problem. The %d and %c format specifiers expect an int to be the parameter, you are only passing a char. Depending on your compiler, this might mean that they are too small.
This is what I think the problem is:
How are you allocating Copy.Array? Are you making sure all its elements are zeroed before you start? If you malloc it (malloc just leaves whatever garbage was in the memory it returns) and an element just happens to contain 0xFF, your loop will exit prematurely because your while condition tests Copy.Array[X] before you have placed a character in that location.
This is one of the few cases where I allow myself to put an assignment in a condition because the pattern
int c;
while ((c = fgetc(fileStream)) != EOF)
{
doSomethingWithC(c);
}
is really common
Edit
Just read your "Additionally" comment. I think it is highly likely you are overrunning your output buffer. I think you should change your code to something like:
int X = 0; int C = 0; int I = 0;
while(X < arraySize && (C = fgetc(Read)) != EOF)
{
Copy.Array[X] = C;
printf("%d %c\n", (int)C, (int)C);
X++;
}
printf("\n");
Note that I am assuming that you have a variable called arraySize that is set to the number of characters you can write to the array without overrunning it. Note also, I am not writing the EOF to your array.
You probably have some heap corruption going on. Without seeing code it's impossible to say.
Not sure if this is your error but this code:
C = fgetc(Read);
Copy.Array[X] = C;
if(C == EOF){break;}
Means you are adding the EOF value into your array - I'm pretty sure you don't want to do that, especially as your array is presumably char and EOF is int, so you'll actually end up with some other value in there (which could mess up later loops etc).
Instead I suggest you change the order so C is only put in the array once you know it is not EOF:
C = fgetc(Read);
if(C == EOF){break;}
Copy.Array[X] = C;
Whilst this isn't what I'd call a 'complete' answer (as the bug remains), this does solve the 'observer effect' element: I found, for some reason, printf was somehow 'fixing' the code, and using std::cout seemed to (well, I can't say 'fix' the problem) prevent the observer effect happening. That is to say, use std::cout instead of printf (as printf is the origin of the observer effect).
It seems to me that printf does something in memory on a lower level that seems to partially correct what does indeed seem to be a memory allocation error.
I'm a total C n00b trying to teach myself C off K&R. My question is kind of embarrassingly elementary. OK, here goes: I can't get programs using getchar to give the kind of output I expected. If you happen to have K&R on hand, I'm stuck on exercise 1.13. The question goes, "Write a program to print a histogram of the lengths of words in its input. " and I can't even tackle the horizontal version because of this issue I'm having.
I'm on XP using Dev-C++ (mingW compiler) and running programs off the command line. My issue is, when I try to run my program, it waits for me to enter characters to scan from, but when I'm done inputting and hit Enter, it doesn't do anything. I expect it to go ahead and print the histogram as I expected. In reality, it doesn't even seem to count up word lengths, because as you can see in the code, when I try to print what's in the ctr array just to see if it contains anything, nothing prints.
I'm so n00b that I have no idea if it's my code or the command line that's at fault. But I suspect it's something with the system, because when I try to compile and run a model program, the same thing happens. Type in input, hit Enter, nothing happens. If I Ctrl-C, sometimes it spits out an asterisk or two that looks nothing like the model output. Other times, it doesn't do anything (just goes right back to the prompt).
Here's my code for the exercise. I've spent an entire day on this and am questioning my ability to carry on with programming. I'd really, really appreciate it if anyone could get me out of this hole!
Also, I have another question about the model program I mentioned above, but I think I should post it in its own question. Thanks all :)
#include <stdio.h>
//#define 1 IN
//#define 0 OUT
int main () {
//start w/ state = OUT
int c = 0;
// int state = OUT;
int len = 0;
int ctr[12];
int i, j;
i = j = 0;
for (i = 0; i <12; i++)
ctr[i] = 0;
while ((c = getchar()) != EOF)
if (c != ' ' && c != '\t' && c != '\n') {
// state = IN;
len++;
printf("%d", len);
}
else {
ctr[len]++;
len = 0;
}
for (i = 0; i <12; i++)
printf("%d\n", ctr[i]);
for (i = 0; i <12; i++) {
printf("%d\n", i);
for (j = 0; j <= ctr[i]; j++)
printf("-");
printf("\n");
}
return 0;
}
Your while loop is looking for EOF which stands for end-of-file, not end-of-line.
On Windows, you need to type ctrl-z to simulate end-of-file.
It doesn't look like you're actually storing c (your input) anywhere... nor printing it. You're printing the size of the string, but not the actual characters. If you ctr[i] = c; somewhere (that's you adding the character to the array), and then print the array, you'll see your input. Oh and yes, the answer about ctrl-z is also important.
Also, if you're totally new to the language, I would strongly urge you to put brackets around your while content. It's going to be a while before you can just glance at the code and know what will fall into the purview of the while loop and what will not if you don't have braces around it.
I didn't see anything really wrong with the code, so I loaded it up under gcc and it seems to work fine, as long as you remember that you have to put in an EOF (CTRL-D) to terminate the while loop. I entered 4 lines and while I can't make any statements about the correctness of the answers, each time I hit enter I got a series of numbers equal to the number of characters I entered, followed by a space. This is exactly what your code says to do.
When I entered the CTRL-D, I got the summary information. Again, I'm not going to make any statements about the correctness of the output, but I did get two major sections as described in your code.