You are not logged in.
Ignore the first part of this message as it was cross-posted:
[IGNORE]
I have recently started to learn C programming language from "The C Programming Language" book, but there is something that really bothers me.
This is exercise 1-9:
Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank.
So I tried to write it by myself, but didn't get it right, so I searched online for a solution, and found a few, but I choose to 'explore' the easier one, but I couldn't really understand it.
Here it is:
#include <stdio.h>
/* count lines in input */
int
main()
{
int c, pc; /* c = character, pc = previous character */
/* set pc to a value that wouldn't match any character, in case
this program is ever modified to get rid of multiples of other
characters */
pc = EOF;
while ((c = getchar()) != EOF) {
if (c == ' ')
if (pc != ' ') /* or if (pc != c) */
putchar(c);
/* We haven't met 'else' yet, so we have to be a little clumsy */
if (c != ' ')
putchar(c);
pc = c;
}
return 0;
}
It is from this page: http://users.powernet.co.uk/eton/kandr2/krx109.html
Can you please explain it for me?
Thanks, and God bless!
[/IGNORE]
I will post my problems with the C exercises from K&R book here in this thread, so that I will not open a new thread for every new problem that I run into.
Last edited by exapplegeek (2012-07-10 13:10:21)
Soli Deo Gloria!
I Stand Up For Christ! Do you?
________________________
Always have your stuff when you need it with Dropbox. Sign up for free (under my referral link)! http://db.tt/3rOCPK4r
Offline
If your goal is to learn the language, having someone explain this piece of code in full isn't going to help you very much. Making an initial effort and then asking pointed questions about the remaining bits is far more productive.
What specifically is unclear about this?
Offline
And post your failed attempt.
Offline
If your goal is to learn the language, having someone explain this piece of code in full isn't going to help you very much. Making an initial effort and then asking pointed questions about the remaining bits is far more productive.
What specifically is unclear about this?
The thing that is unclear is simply that I don't understand how it replaces ' ' with simply a ' ' for example. I need to understand how it works.
It reads the data from "c" and then it prints it again (if the condition is met), but I don't see how it changes the data inside "c".
And I don't undestand how the "pc" works.
I didn't save my (failed attempt) code, but I think I can rewrite it, but it's simply too stupid to base my solution on it.
If you want, I will rewrite it and post it here.
Last edited by exapplegeek (2012-07-08 22:58:36)
Soli Deo Gloria!
I Stand Up For Christ! Do you?
________________________
Always have your stuff when you need it with Dropbox. Sign up for free (under my referral link)! http://db.tt/3rOCPK4r
Offline
Disclaimer: I have never used C, but I can program in java, haskell and prolog.
Although this is a particularly bad program for such an easy task and the code speaks for itself, I will try to explain it anyway. Note, EOF=end of file.
The code reads every character from the input. The character is kept in c. The previous character is kept in pc. Unless both c and pc are a space, it will print it.
Note that the condition in the while loop both reads a new character and checks if the character is different from EOF.
example, input: "012 3"
1) pc=EOF c='0' --> print '0' (c!=' ' succeeds)
2) pc='0' c='1' --> print '1'
3) pc='1' c='2' --> print '2'
4) pc='2' c=' ' --> print ' ' (c==' ' succeeds and pc!=' ' succeeds)
5) pc=' ' c=' ' --> don't print anything (both c and pc are ' ')
6) pc=' ' c='3' --> print '3'
7) pc='3' c=EOF --> Stop
--> The while loop prints "012 3"
The code is really bad because of 3 things:
1) The code returns an integer (a number), 0, while in has nothing to do with numbers.
2) Characters are represented by integers (again, numbers), instead of chars (letters). While this is possible, it is ugly.
3) The code in the loop is too long for what it does.
The code should probably be written as:
#include <stdio.h>
void main()
{
char pc = EOF;
char c= getchar();
while (c != EOF) {
if (c != ' ' || pc != ' ')
putchar(c);
pc = c;
c = getchar();
}
}
Offline
Write a sentence with a just a few words in it. Put a 2 or 3 spaces between some of the words. Then go through the sentence using "pseudo-code" to understand the "flow" of the program. Then go through the sentence again using the actual code. For example, let's say I am typing the sentence
I saw a red truck.
So in psuedo-cdoe we have:
Read a character from input, until EOF is reached.
If it's a space, check to see if previous character is a space.
If previous character is not a space, echo character (otherwise do not echo additional spaces)
If it's not a space, echo character
Always try and write your program in pseudo-code first.
john
Offline
Here is my solution, with a different approach. Perhaps this makes more sense to you.
#include <stdio.h>
/* replace all sequences of blanks with a single blank. */
int main()
{
int c;
int series_of_blanks = 0;
while ((c = getchar()) != EOF) {
if (c == ' ') {
if (!series_of_blanks) {
series_of_blanks = 1;
putchar(c);
}
} else {
series_of_blanks = 0;
putchar(c);
}
}
return(0);
}
Edit: Just read the linked thread, my solution is much like the first one. To get a better understanding of this problem, look at the positions in the code where and under which conditions a character is printed out.
Last edited by drm00 (2012-07-09 19:42:01)
Offline
Oy vey.
Disclaimer: I have never used C, but I can program in java, haskell and prolog.
Then why are you trying to correct other people's code?
The code is really bad because of 3 things:
1) The code returns an integer (a number), 0, while in has nothing to do with numbers.
2) Characters are represented by integers (again, numbers), instead of chars (letters). While this is possible, it is ugly.
3) The code in the loop is too long for what it does.The code should probably be written as:
#include <stdio.h> void main() { char pc = EOF; char c= getchar(); while (c != EOF) { if (c != ' ' || pc != ' ') putchar(c); pc = c; c = getchar(); } }
1) main() always returns an int, which is the exit status of the program. People who write void main() in C don't know what they're doing. return 0; from main signifies a normal status and is completely standard and correct.
2) getchar() returns an int, and storing the result as a char eliminates your ability to test for end-of-file. EOF is defined to be -1, which as an int can be distinguished from a valid character. On the other hand, the char value -1, namely 0xff, is a perfectly valid constituent of a file.
3) This is completely subjective, and "length of source code" is a poor metric of quality, in any case.
P. S. The OP is a cross post.
Offline
The thing that is unclear is simply that I don't understand how it replaces ' ' with simply a ' ' for example. I need to understand how it works.
It reads the data from "c" and then it prints it again (if the condition is met), but I don't see how it changes the data inside "c".
And I don't undestand how the "pc" works.
First, remember what it is supposed to do:
Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank.
So if you have the input string "foo bar baz whatever", then you want to copy it to "foo bar baz whatever".
What you are really doing is removing extra blanks (" "). If there is only one blank in a row, then you can just pass it through. If there are two or more blanks in a row, you only want to pass one through. Everything that is not a blank gets passed through.
Another way to think of it is that a blank can only go through if the last character was not a blank.
So for each character of the input, if it is not a blank, you let it through.
If it is a blank and the last character was not a blank, you let it through.
If it is a blank and the last character was also a blank, then you throw it away. You do this until you get to a non-blank character.
"pc" is the previous character. It keeps track of the last character so you know what to do with the blanks.
Does that help at all?
My Arch Linux Stuff • Forum Etiquette • Community Ethos - Arch is not for everyone
Offline
Oy vey.
Terminator wrote:Disclaimer: I have never used C, but I can program in java, haskell and prolog.
Then why are you trying to correct other people's code?
Maybe he intends to become the next Herbert Schildt.
Offline
...
1) main() always returns an int, which is the exit status of the program. People who write void main() in C don't know what they're doing. return 0; from main signifies a normal status and is completely standard and correct.
...
Well, always is a pretty broad brush. Granted, on a program designed to run on a system with an operating system, it is probably true. There are, however, those of us who write firmware for embedded systems. If main ever returns, you have done something very, very wrong. Sometimes, the stack does not even get set up until you are in the main code. Hopefully there is a good watchdog timer to provide a non-graceful recovery should main return. As such, we use void main(void) to explicitly say we don't return anything, and we don't expect any parameters on the stack.
I'm just saying ...
Nothing is too wonderful to be true, if it be consistent with the laws of nature -- Michael Faraday
Sometimes it is the people no one can imagine anything of who do the things no one can imagine. -- Alan Turing
---
How to Ask Questions the Smart Way
Offline
Lux Perpetua wrote:...
1) main() always returns an int, which is the exit status of the program. People who write void main() in C don't know what they're doing. return 0; from main signifies a normal status and is completely standard and correct.
...Well, always is a pretty broad brush. Granted, on a program designed to run on a system with an operating system, it is probably true. There are, however, those of us who write firmware for embedded systems. If main ever returns, you have done something very, very wrong. Sometimes, the stack does not even get set up until you are in the main code. Hopefully there is a good watchdog timer to provide a non-graceful recovery should main return. As such, we use void main(void) to explicitly say we don't return anything, and we don't expect any parameters on the stack.
I'm just saying ...
Okay, fair enough. I do recall that the standard distinguishes between "hosted" and "freestanding" environments, the requirements on main() applying to the former and basically going out the window for the latter. (I think that in the freestanding case, the entry point doesn't even have to be called "main()".) But it's safe to assume by default that the OP's program is intended for a hosted environment (indeed, it's probably intended to be portable ANSI-compliant code).
Offline
I have closed the thread on devshed, but will keep this open for further questions.
Soli Deo Gloria!
I Stand Up For Christ! Do you?
________________________
Always have your stuff when you need it with Dropbox. Sign up for free (under my referral link)! http://db.tt/3rOCPK4r
Offline
Write a sentence with a just a few words in it. Put a 2 or 3 spaces between some of the words. Then go through the sentence using "pseudo-code" to understand the "flow" of the program. Then go through the sentence again using the actual code. For example, let's say I am typing the sentence
I saw a red truck.
So in psuedo-cdoe we have:
Read a character from input, until EOF is reached. If it's a space, check to see if previous character is a space. If previous character is not a space, echo character (otherwise do not echo additional spaces) If it's not a space, echo character
Always try and write your program in pseudo-code first.
john
Thanks about this, this helped me when solving the next exercise, and I see it as an advice from an experienced programmer, and I think it will be of a great use.
On the other side, speaking about "void main()" it caused me some confusion, but in the past, when I was coding in Xcode (back when I had OS X) it gave me some warning, and from that time till now, I thought that "void main()" was obsolete and replaced by "int main()". But now I see in the book that it is even possible to write it as "main()".
Thanks God, I successfully wrote the next program (exercise 1-12), though it was not very successful on the first attempt, I did a lot of tweaking, and I was going to ask here what I was doing wrong why it printed new lines for every blank, but I looked at the solution from that site, and I didn't copy it, but tweaked my code, and finally got it working.
I will also change the name of the thread, so I will not post new threads for every 'difficult' exercise in K&R.
Soli Deo Gloria!
I Stand Up For Christ! Do you?
________________________
Always have your stuff when you need it with Dropbox. Sign up for free (under my referral link)! http://db.tt/3rOCPK4r
Offline
Well, always is a pretty broad brush. Granted, on a program designed to run on a system with an operating system, it is probably true. There are, however, those of us who write firmware for embedded systems. If main ever returns, you have done something very, very wrong. Sometimes, the stack does not even get set up until you are in the main code. Hopefully there is a good watchdog timer to provide a non-graceful recovery should main return. As such, we use void main(void) to explicitly say we don't return anything, and we don't expect any parameters on the stack.
Somebody always brings this up when void main() comes up, and, while true, it isn't useful. People who use freestanding implementations generally know it. Given that this is a general programming forum not dedicated to any freestanding implementation, the OP didn't name any particular implementation, and the code in question uses <stdio.h> and no strictly conforming freestanding implementation is required to do anything sensible or even output-related with those functions, supposing a freestanding environment merely confuses the issue.
Offline
On the other side, speaking about "void main()" it caused me some confusion, but in the past, when I was coding in Xcode (back when I had OS X) it gave me some warning, and from that time till now, I thought that "void main()" was obsolete and replaced by "int main()". But now I see in the book that it is even possible to write it as "main()".
In C89, a function without an explicitly declared return type is implicitly int, so main() is the same as int main(). C99 got rid of the implicit-int rule, so under C99 you have to use int explicitly. It's good practice anyway.
void main() is a non-standard extension supplied by some compilers that gained popularity by being used by a number of popular reference books on C. It's not "obsolete" because it was never part of C in the first place.
Offline
.
void main() is a non-standard extension supplied by some compilers that gained popularity by being used by a number of popular reference books on C. It's not "obsolete" because it was never part of C in the first place.
Interesting, I thought that was just some trash that was only MSVC compatible. For firmware/embedded systems it makes sense, but for a program being loaded into memory on an O.S.?
I honestly am GCC strictly guy; I'll MSVC occasionally, but that's usually if it's my only option.
Offline
I have closed the thread on devshed, but will keep this open for further questions.
No need to go that far! You were getting some really good replies on Dev Shed. I'd say there's nothing wrong with cross-posting to multiple forums on different websites as long as you link to the cross posts. It saves people from wasting effort posting the same answer someone else posted on a different forum.
(Unfortunately, this level of etiquette is very rare. Typically, people not only cross-post their question all over the web, but they don't even bother to follow up when answers are posted.)
http://www.youtube.com/watch?v=v3rhQc666Sg
Last edited by Lux Perpetua (2012-07-12 00:22:29)
Offline
The exercise 1-13 is a bit complicated, and I don't know how to acheive one thing.
Exercise 1-13:
Write a program to print a histogram of the lengths of words in its input. It is easy to draw the
histogram with the bars horizontal; a vertical orientation is more challenging.
Here is my current code:
#include<stdio.h>
#define OUT 0
#define IN 1
main() {
int c, i1, i2, nchar, ndigit, nword, state;
nchar = ndigit = nword = 0;
printf("Please enter word(s):\n");
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nword;
}
if (c >= '0' && c <= '9')
++ndigit;
if (c != ' ' && c != '\n' && c != '\t') {
++nchar;
}
}
putchar('\n');
for (i1 = 0; i1 <= nword; ++i1) {
putchar('|');
putchar('\n');
}
for (i2 = ndigit; i2 < nchar; ++i2)
putchar('-');
putchar('\n');
}
If my input is like this:
word1 word2 word3
It's output would be like this:
|
|
|
------------
What I am trying to acheive is at least this:
|----
|----
|----
Note: '|' - denotes a word; while '-' denotes its length.
Soli Deo Gloria!
I Stand Up For Christ! Do you?
________________________
Always have your stuff when you need it with Dropbox. Sign up for free (under my referral link)! http://db.tt/3rOCPK4r
Offline
have a look on both for loops. For the moment the first one just prints every '|\n' and the second one produces '----' four times in a row. Just put them together and it should work (and save you some code ). *
And you really can make it work with just one variable. Try
* sorry, didn't look properly, there is a bit more to do which you'll see after you made the diagram work...
Last edited by null (2012-07-12 18:55:01)
Offline
The statistician in me just died a little: that's not a histogram.
Do you just want to graphically represent the length of words, or do you want an actual histogram? For the latter you could do something following the logic:
1) create array of integers with length up to the max word-size you'd care about, for this example I'll call it counts[]
2) loop through the input counting (non-whitespace) characters until the first whitespace character, add one to the appropriate member of the above array (eg counts[count]++).
3) repeat (2) until end of input is reached
4) loop through counts displaying each value as a column with a height equal to it's value.
edit: some pseudo-code
decalare counts[MAX_WORDSIZE] as integer
while not end of input
set count to 0
get character
while not whitespace
increment count
get character
end while
increment counts[count]
end while
for i in 1 to MAX_WORDSIZE
print "|"
for j in 1 to counts[i]
print "-"
end for
print <newline>
end for
Last edited by Trilby (2012-07-12 18:46:49)
"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman
Offline
Trilby: Very nice solution. And written in pseudo-code. Since your solution employs nested loops, it makes me wonder if our student has been introduced to this structure yet. His solution, while not as elegant as yours, is really only missing this key idea.
john
P.S. I can't tell you how many times I've seen bar charts - clearly labeled with qualitative data - being described as histrograms. And in high school AP statistics books, they don't even show you how to create a histogram - everything is done on the TI-83. Just for fun, I show them how to do one by hand.
Offline
Mine also is hardly a histogram. It is if there is a bin for each additional letter, which I suppose for human readable text is probably a good size.
"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman
Offline
I have rewritten the code, following the advice of Trilby (but I didn't make it exactly like his solution), but it gives me some very long incorrect output.
Can you take a look and tell me what's wrong?
#include<stdio.h>
#define OUT 0
#define IN 1
main() {
int c, i, j, nchar, ndigit, nword, state;
int array[64];
nchar = ndigit = nword = 0;
printf("Please enter word(s) (max 64 chars):\n");
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nword;
}
if (c >= '0' && c <= '9')
++ndigit;
if (c != ' ' && c != '\n' && c != '\t') {
++nchar;
}
++array[nchar];
}
putchar('\n');
for (i = 1; i <= nword; ++i) {
putchar('|');
for (j = ndigit; j < array[i]; ++j)
putchar('-');
putchar('\n');
}
}
Soli Deo Gloria!
I Stand Up For Christ! Do you?
________________________
Always have your stuff when you need it with Dropbox. Sign up for free (under my referral link)! http://db.tt/3rOCPK4r
Offline
Initialize your array
Nothing is too wonderful to be true, if it be consistent with the laws of nature -- Michael Faraday
Sometimes it is the people no one can imagine anything of who do the things no one can imagine. -- Alan Turing
---
How to Ask Questions the Smart Way
Offline