K&R Exercise 1-21 - Mental incomprehension - c

The "impossible" K&R exercise.
"Write a program entab that replaces
strings of blanks by the minimum
number of tabs and blanks to achieve
the same spacing. Use the same tab
stops, say every n columns. Should n
be a variable or a symbolic
parameter?"
The problem I'm having is, I'm unsure about how to even do this correctly. I know it's not very explanatory, but that's pretty much the problem here. Most of the examples I've seen have counted a number of blanks, and replaced those series with a tab, but this isn't what its asking, I reckon I understand what its asking, but currently feel unable to do this.
Could anyone help :)
Edit: The code I've written so far can be found here.

If your question is "What is this asking me to do?" I think I can help by paraphrasing the original question (posing the same question in a different way).
Write a program that takes as input text with spaces and produces as output visually equivalent text using tabs to the maximum extent possible.
For example, with tabstops every 8 characters, and showing spaces as '.' and tabs as '-';
input;
".foo:...bar;......#comment"
output;
".foo:-bar;-..#comment"
input;
".......-foo:.....bar;......#comment"
output;
"-foo:-.bar;-...#comment"
Write the program so that tabstop parameter n can be varied, i.e. allow values of n other than 8. Be prepared to justify your decision to make n a constant, or alternatively a variable.
Edit I had a look at your code and I think it is more complex than it needs to be. My advice is to do it a character at a time. There's no need to buffer a whole line. Maintain a column count as you read each character ('\n' resets it to zero, '\t' bumps it by 1 or more, other characters increment it). When you see a space (or tab), don't emit anything right away, start your entabbing process, emit zero or more tabs and then spaces later (at '\n' or a non whitespace character, whichever comes first).
A final hint is that a state machine can make this kind of algorithm a lot easier to write, validate, test and read.
Edit 2 In a shameless attempt to get the OP to accept my answer, I have now gone ahead and actually coded a solution myself, based on the hints I offered above and my comment in the discussion.
// K&R Exercise 1-21, entab program, for Stackoverflow.com
#include <stdio.h>
#define N 4 // Tabstop value. Todo, make this a variable, allow
// user to modify it using command line
int main()
{
int col=0, base_col=0, entab=0;
// Loop replacing spaces with tabs to the maximum extent
int c=getchar();
while( c != EOF )
{
// Normal state
if( !entab )
{
// If whitespace goto entab state
if( c==' ' || c=='\t' )
{
entab = 1;
base_col = col;
}
// Else emit character
else
putchar(c);
}
// Entab state
else
{
// Trim trailing whitespace
if( c == '\n' )
{
entab = 0;
putchar( '\n' );
}
// If not whitespace, exit entab state
else if( c!=' ' && c!='\t' )
{
entab = 0;
// Emit tabs to get close to current column position
// eg base_col=1, N=4, col=10
// base_col + 3 = 4 (1st time thru loop)
// base_col + 4 = 8 (2nd time thru loop)
while( (base_col + (N-base_col%N)) <= col )
{
base_col += (N-base_col%N);
putchar( '\t' );
}
// Emit spaces to close onto current column position
// eg base_col=1, N=4, col=10
// base_col -> 8, and two tabs emitted above
// base_col + 1 = 9 (1st time thru this loop)
// base_col + 1 = 10 (2nd time thru this loop)
while( (base_col + 1) <= col )
{
base_col++;
putchar( ' ' );
}
// Emit buffered character after tabs and spaces
putchar( c );
}
}
// Update current column position for either state
if( c == '\t' )
col += (N - col%N); // eg col=1, N=4, col+=3
else if( c == '\n' )
col=0;
else
col++;
// End loop
c = getchar();
}
return 0;
}

I'm a bit late, but here's how I solved it myself. It's a different approach than what has been shared above, so please share any comments/feedback if you have any.
Check out my public gist on Github for the source code. There's comments on the code, and the approach is explained on the top of the file, but I'll copy and paste it here just so that the logic is clear from the get-go.
Approach:
We'll keep track of number of spaces encountered (between nontab/nonspace characters)
We'll keep track of characters (that aren't tabs/blanks/newlines) per input line
We'll evaluate the "gaps" generated by spaces by:
Evaluating whether the number of spaces in between those characters.
A gap will be "big enough" when the number of spaces is >= TABSIZE
Then, for all the left over spaces in our "buffer", we'll print them out individually
Finally, we print out the character that was read in (which was not a tab/blank)
As well as updating space count and character count if necessary.
I'm still a novice programmer in all senses, so I am not sure of how it would compare vs the other solutions posted in here, but the logic seems easier to follow (at least for me).
Hope this helps someone later on!

I agree with your assessment. It won't be enough to replace every n blanks with a tab; for example, if n == 4, "hi blank blank blank blank" should not be replaced by "hi tab", but rather by "hi tab blank blank".
It sounds like what you need to do is keep track of the current position as you're reading in each line, and use this information to determine how many tabs you need. Does this help? Please let me know if you need more details!
As for the "variable vs. symbolic parameter" part, either would definitely be viable, but I can think of one significant advantage to using a variable: you can run the program for different values of n without recompiling.

My understanding is that you don't really have to know what the problem is or how to solve it in order to answer this question. The question seems to asking whether you understand when to use variables instead of "symbolic parameters". I'm not actually sure what is meant by "symbolic parameter"; it seems to be outdated nomenclature.
Having said that, solving the first part of the question (replacing spaces with tabs) is rather straight forward. Think division and remainders.

I took a very cursory look at your code, and nothing is jumping out at me as blatantly wrong.
So my advice would be to either single-step through a few input examples in a debugger, examining variable values as you go, or add a whole bunch of debugging print statements. In either case, your goal is to find the point where the state of the program starts to deviate from what you expected or intended.

I am currently plowing KnR and came across this page:
Answers to Exercises
Your exercise are located under:
Solutions
Chapter 1 - A Tutorial Introduction
Ex No 21 Pg No 34
users.powernet.co.uk/eton/kandr2/krx121.html
Hopefully you find this useful.
Sincerely,
Morpfh
1: http://users.powernet.co.uk/eton/kandr2/index.html "The C Programming Language", 2nd edition, Kernighan and Ritchie - Answers to Exercises

In the top rated answer above, the program is overly complex.
In an attempt to simplify that part of the answer, I've attached a much simpler code hopefully written in the style of K&R (mostly by incrementing inline with ++).
include
define TAB 4
int main(){
char newsentence[255],c;
int spacecount = 0, oldsentencepointer = 0, newsentencepointer = 0;
printf("Give me a sentence please:\n");
while ((c = getchar()) != '\n') {
if ((oldsentencepointer != 0) && (oldsentencepointer % TAB == 0) && (spacecount > 0))
{
newsentencepointer -= spacecount; //if at tabstop, and spaces and not
first, go back to 1st space, set tab.
newsentence[newsentencepointer++] = '\t';
spacecount = 0;
}
if (c == ' ') {
newsentence[newsentencepointer++] = ' ';
spacecount++; //keep track of spaces before tab stop
}
else if (c == '\t') {
newsentence[newsentencepointer++] = '\t' ;
oldsentencepointer = TAB; //set old pointer to TAB (does not matter if actual,
only cadence important)
continue; //continue from here so as not to increment
old sentence counter.
}
else {
newsentence[newsentencepointer++] = c ; //write whatever was old into new.
spacecount = 0; //reset space counter.
}
oldsentencepointer++;
}
newsentence[newsentencepointer] = '\0'; //cap it off.
puts(newsentence);
return 0;
}

There is an even more concise solution, although it does not employ the best code practices available (abusing short circuit evaluation, awkward control flow via continue, somewhat weird "space" loop).
#include <stdio.h>
#define TS 8
int main(int arg, char *argv[]) {
int counter = 0, space_counter = 0, c;
while ((c = getchar()) != EOF) {
++counter;
if (c == ' ' && ++space_counter && (counter % TS) == 0) {
space_counter = 0;
c = '\t';
} else if (c == '\t') {
counter = space_counter = 0;
} else if (c != ' ') {
while (space_counter--)
putchar(' ');
space_counter = 0;
if (c == '\n')
counter = 0;
} else {
continue; /* don't call putchar(c) */
}
putchar(c);
}
return 0;
}
Except for blanks, every character that is read is printed verbatim. Blanks are counted instead. If the program encounters a non-blank character, it prints as many blanks as it has counted before, resetting that counter afterwards. If it encounters a blank, it checks via a second counter (printed characters since the beginning of the line/last tabstop) if the cursor is on a tabstop. If it is, a tab is printed, otherwise the blank is just counted.
A tab in the input is dealt with resetting the space counter and outputting the tab, eliminating any superfluous blanks in the process.

Related

Array-member comparison in a function not working

First of all I'm not even sure whether you call it a member, couldn't think of a better term.
I'm trying to learn basics of debugging and arrays - so I wanted to create something resembling insert-sort from memory (so mistakes would be made) and then debug the program.
void findingsmaller (int *array, int num_inputs){
int a = 0;
int b = 1;
for ( b=1; b == num_inputs-1; b++ ) {
if ( array[a] > array[b] ) {
goleft(array, a, b);
a++;
}
}
}
Let's say we have this in array: 6 5 3 1 8 7 2 4. array[a] should be 6 and array[b] should be 5. 6 > 5 so we should enter the function that would find the first smaller number on the left of the array.
From my debugging session it seems like the condition is FALSE so I don't enter goleft at all. More specifically, Step into ignores it, the testing printf wasn't executed either. I'm assuming the array comparison is not written properly, what's the correction?
WHOLE CODE if somebody wants to see other possible mistakes.
Thank you in advance!
EDIT: <= num_inputs is correct, somehow I thought for has (range1, range2, change) instead of (start, condition, change). Anyway, now the problem seems that my goleft function does its do-while cycle one time too many although it shouldn't get past that condition.
EDIT2: A couple of other mistakes were fixed.
My printing in main is now for( ; i <= num_inputs-1; )
My goleft would do too many iterations due to the condition, fixed into ... while ( a >= 0 && array[a] > array[b] )
My findingsmaller would only operate if the number next is smaller but does nothing when the number is greater. For example for 6 8 the program wouldn't function properly. Added else {a++}
My fixed code for anyone interested in the comparison of the changes.
The for loop is executed as long as the condition is True.
for ( ;Condition; )
{
// body
}
In your for loop, the condition is always False if the input is greater than 1.
Instead of b == num_inputs - 1, you should put b < num_inputs in your for loop condition. Since the equality isn't true on the first iteration of the loop, it is immediately breaking.
I was looking a little bit at your code and i notice something that doesnt work.
while (scanf("%d", &array[i]) != EOF)
As the documentation of the function scanf say :
The return value is EOF for an error
your while condition was making you num_imputs reaching 200 even if there was only 3 inputs. I would replace EOF by '\n'.
while (scanf("%d", &array[i]) != '\n') /* stop when the user input '\n' you
can replace with any character you want to make it stop. */
I did not make a lot of test but this should make your program work fine.

What is the difference between IF and ELSE IF clauses?

I'm on the path of learning C with K&R. Besides the exercises in the book, I'm also doing some by myself. I wrote the following code, what it does is, it counts your input, gives you feedback how many words are left to reach the "goal" of 10 words and congratulates you as soon as you reach the 10 words.
#include <stdio.h>
main()
{
/* This programm will read your input, check the number of words,
and will congratulate when you reach a value of 10 words*/
int c, nw, counter;
nw = 0;
counter = 0;
while (nw < 10)
{
c = getchar();
if (c == ' ' || c == '\t' || c == '\n')
{
counter = 0;
}
else if (c != ' ' || c != '\t' || c != '\n')
{
if (counter == 0)
{
counter = 1;
++nw;
}
printf("Only %d words left\n", 10-nw );
}
}
}
Ok, in this version the code will not count blanks as words, resulting in the correct output of words left.
At first I wrote the code with only "if" instead of "else if". What it did was it also counted the blanks as words.
The question I am asking is why ? Wheres the difference in using if or else if.
As I understand is, that compiler will check whether a condition is met or not met. This should also be the case when only using if. As everything is the same expect else if instead of if, I can't figure out what the problem is.
It looks like this might be a case of overthinking a problem. The logic you've ended up with, aside from being wrong, is overly complicated. Your question of the difference between if and else if is fair, and I promise I will address it in my answer.
First, let me restate what you are trying to do:
Read input, count number of words, and congratulate you when you reach 10 words
From your code, I believe your intention is to split words based on spaces, tabs, and newlines. There are many ways to split words, but for the purposes of this question, your intended method is fine.
The problem, of course, is that your logic doesn't work. You have a condition that can never be false:
else if (c != ' ' || c != '\t' || c != '\n')
Think about it (hint: the else doesn't change the condition itself). Say it out loud if that helps: You are looking for a condition where c isn't a space, or c isn't a tab, or c isn't a newline. Remember that logical or (||) is an inclusive or, in other words, the expression is true if any of the conditions are true. For example, let's suppose c is a space. The first condition (c != ' ') fails, but the 2nd one, c != '\t' is true because a space is not a tab. Thus, the entire expression is true. In fact, that expression will be true for any value of c.
But what about else?
As I said, the else part of the else if doesn't make a difference here. The only thing else if does differently is essentially tack itself on as a new condition to your if statement. Let's look at a simpler example:
if (a == 1) {
/* a is 1 */
}
if (a != 1 && b == 2) {
/* a isn't 1, but b == 2 */
}
That's an example of two completely independent if statements. It's a perfect example of where to use else, because as you probably noticed, the 2nd if statement tests for the inverse of the 1st. (a != 1). So, the above can be simplified as follows:
if (a == 1) {
/* a is 1 */
else if (b == 2) {
/* a isn't 1 and b is 2 */
}
In the else if block, we needn't test for a != 1, as that's implied because we only evaluate the else statement if the 1st if conditional was false.
Note also that else if is actually a combination of two separate keywords. It is equivalent to:
else {
if (b == 2) { ... }
}
However, by convention we omit the optional braces and write it as:
else if (b == 2) { ... }
In fact, in some cases we don't need that 2nd if at all:
if (a == 1) {
printf("a is 1!\n");
} else {
printf("a isn't 1. In fact, it's %d.\n", a);
}
Simplified Version
So, now there's no need to get caught up in else if. Focus on your logic, and do your best to simplify it. I will simplify it for you, however I encourage you to skip over this part and try it on your own first:
char c;
int in_word = 0;
while (nw < 10) {
c = getchar();
if (c == ' ' || c == '\t' || c == '\n') {
/* If we were in a word, then count that word! */
if (in_word) {
nw++;
printf("You only have %d words to go!", 10 - nw);
}
in_word = 0; /* We are not in a word now */
} else {
in_word = 1; /* Now we're in a word
}
}
Not sure if this is the answer you are looking for. But two separate if statements are both evaluated no matter what. With "if" and "else if", first if is evaluated if it is true, else if is skipped. When "if" is not true "else if" is evaluated.
else if (c != ' ' || c != '\t' || c != '\n')
This line of code is probably not doing what you intended. It works fine as an else if, but in fact it doesn't really check anything (you could actually replace it with a simple else)! For any conceivable character, it's will always be either not a space, or not a tab, or not a newline. What you really want is the conjunction of those statements, rather than the current disjunction:
else if (c != ' ' && c != '\t' && c != '\n')
This checks that the character is neither a space, a tab, nor a newline. It would work even with else removed, as a separate if-statement. However, you really should just go with a simple else.
In your if condition, if you use simple if .. else construct, it will be equivalent to
if (c == ' ' || c == '\t' || c == '\n')
{
...
}
else if (c != ' ' && c != '\t' && c != '\n') //Notice &&.
//Apply De-morgan's law over if conditio.
{
...
}
In the above case else if is not required, if else is suffice to use. Using if .. else would be more efficient than the given code.
However, logically it may have same effect as your given code.
As per logic, else if is not required.
The difference you will get in terms of number of comparison operation as explained below.
Current code:
For any non-space character such as 'a', it will perform 4 comparisons (|| and && are short-circuited).
For any space character, there will be max 3 comparisons.
In if-else code, max of 3-comparison will be performed.
This difference is not so significant. And can only be noticed when code is run billions of times.
if else has extra advantage of maintenance. In the above code in OP, if there is any change in if part, it should be reflected in else if part also.
Hence, to me it seems it will be beneficial to go with just if else rather if else if.

fgetc() doesn't work properly

I have file that goes like 7 4 5 1 etc. I want to put these number in a multi-dimensional array.
for(x=0;x<9;x++)
for(y=0;y<9;y++)
{
current=fgetc(fp);
if(current!=EOF&current!=' '&current!='\n')
sudokuArray[x][y] = current-'0';
}
This code doesn't work properly. sudokuArray[0][0] gives the result true(first number in the file) but [0][1] gives some random number like 131231304. [0][2] gives what [0][1] supposed to be. Why is that?
It's because if your inner loop:
for(y=0;y<9;y++)
{
current=fgetc(fp);
if((current!=EOF) && (current!=' ') && (current!='\n'))
sudokuArray[x][y] = current-'0';
}
If your input is 7 4 5 1, then when y == 0, you read '7', which gets put in sudokuArray[0][0].
Now on your next loop, y == 1, and you read ' '. Because of your if statement, you don't put that in sudokuArray but y still gets incremented.
Next time you do the loop, y == 2, you read '4' and it gets put in sudokuArray[0][2].
So fgetc() does work properly and your code is doing exactly what you told it to do. Code is very obedient that way.
Edit: Also note your if statement should contain && instead of &. They are different operators. A little whitespace and some parenthesis make the code easier to read and maintain as well.
I am not sure why you use nested loops to read from file. I see one problem in your code:
Change & to &&:
if(current!=EOF&current!=' '&current!='\n')
to:
if(current!=EOF && current!=' ' && current!='\n')
&& is the logical AND and & is bitwise AND.
Since, you have only integers and you seem to know the exact no. of integers. You can simply use fscanf:
fscanf(fp, "%d", &arr[x][y]);
Inside your loop you're ignoring characters - but then you're incrementing the indexes even though you haven't filled the related array element. Try re-implementing as:
#include <stdlib.h>
for(x=0;x<9;x++)
for(y=0;y<9;y++)
{
/* Get next character */
current=fgetc(fp);
/* Loop, retrieving additional chars, until either EOF is hit or
you find a non-whitespace character */
while(current != EOF && iswhite(current))
current = fgetc(fp);
if(current != EOF)
sudokuArray[x][y] = current - '0';
}
Also - do you want to check current != EOF, or should this be !feof(fp)?
Share and enjoy.

Counting Syllables one char at a time [C]

I'm writing a program which reads text from a file, and determines the number of sentences, words, and syllables of that file. The trick is, it must only read one character a time, and work with that. Which means it can't just store the whole file in an array.
So, with that in mind, heres how my program works:
while(character != EOF)
{
check if the character is a end-of-sentence marker (?:;.!)
check if the character is whitespace (' ' \t \n)
(must be a letter now)
check if the letter is a vowel
}
Using a state-machine approach, each time the loop goes through, certain triggers are either 1 or 0, and this effects the count. I have had no trouble counting the sentences or the words, but the syllables are giving my trouble. The definition for syllable that I am using is any vowel or group of vowels counts as 1 syllable, however a single e at the end of a word does not count as a syllable.
With that in mind, I've created code such that
if character = 'A' || 'E' ... || 'o' || 'u'
if the last character wasnt a vowel then
set the flag for the letter being a vowel.
(so that next time through, it doesnt get counted)
and add one to the syllable count.
if the last character was a vowel, then dont change the flag and don't
add to the count.
Now the problem i have, is my count for a given text file, is very low.
The given count is 57 syllables, 36 words, and 3 sentences. I get the sentences correct, same with the words, but my syllable count is only 35.
I also have it setup so that when the program reads a !:;.? or whitespace it will look at the last character read, and if that is an e, it will take one off the syllable count.
This takes care of the e being at the end of a word not counting as a vowel.
So with this in mind, I know there must be something wrong with my methodology to get such a vast difference. I must be forgetting something.
Does anyone have some suggestions? I didn't want to include my entire program, but I can include certain blocks if necessary.
EDIT: Some code...
I have if ( end-of-sentence marker), then else if (whitespace), then the final else which entails that only letters which can form words will be in this block. This is the only block of code which should have any effect on the counting of syllables...
if(chrctr == 'A' || chrctr == 'E' || chrctr == 'I' || chrctr == 'O' || chrctr == 'U' || chrctr == 'a' || chrctr == 'e' || chrctr == 'i' || chrctr == 'o' || chrctr == 'u')
{
if(chrctr == 'E' || chrctr == 'e')
{
isE = 1;
}
else
{
isE = 0;
}
if(skipSylb != 1)
{
endSylb = 1;
skipSylb = 1;
}
else
{
endSylb = 0;
skipSylb = 1;
}
}
else
{
endSylb = 0;
skipSylb = 0;
}
So to explain... endSylb if 1, later in the program will add one to the count of syllables. skipSylb is used to flag if the last character was also a syllable. If skipSylb = 1, then this is a block of vowels and we only want to add one to the counter. Now I have an isE variable, which just tells the program next time around that the last letter was an E. This means, next time through the while loop, if it is an end of sentence, or whitespace, and the last letter was E (so isE = 1), then we have added one too many syllables.
Hopefully that helps.
Since the value is actually lower then what it should be, i thought perhaps the statements where i minus from the count are important too.
I use this if statement to decide when to minus from the count:
if(isE == 1)
{
countSylb --;
}
This statement happens when the character is whitespace, or an end of sentence character.
I can't think of anything else relevant, but i still feel like im not including enough.
Oh well, let me know if something is unclear.
I also have it setup so that when the program reads a !:;.? or whitespace it will look at the last character read, and if that is an e, it will take one off the syllable count.
This sounds wrong. What about words like "die" and "see"?
Obviously you can only decrement the count if the word counted for more than one syllable.
In your case decrementing if the 'e' at the end was not part of a vowel group might suffice.
If that doesn't help: Maybe you don't clear the vowel flag after reading a consonant? I can't tell from your code.
What could really help you is debugging outputs. Let the program tell you what it is doing like:
"Read a vowel: e"
"Not counting the vowel e because [...]"
You need a Finite State Machine
In a sense, every program is a state machine, but typically in the programming racket by "state machine" we mean a strictly organized loop that does something like:
while (1) {
switch(current_state) {
case STATE_IDLE:
if (evaluate some condition)
next_state = STATE_THIS;
else
next_state = STATE_THAT;
break
case STATE_THIS:
// some other logic here
break;
case STATE_THAT:
// yet more
break;
}
state = next_state;
}
Yes, you can solve this kind of program with general spaghetti code. Although legacy spaghetti code with literal jumps isn't seen any more, there is a school of thought which resists grouping lots and lots of conditionals and nested conditionals in a single function, in order to minimize cyclomatic complexity. To mix metaphors, a big rat's-nest of conditionals is kind of the modern version of spaghetti code.
By at least organizing the control flow into a state machine you compress some of the logic into a single plane and it becomes much easier to visualize the operations and make individual changes. A structure is created that, while rarely the shortest possible expression, is at least easy to modify and incrementally alter.
Looking at your code, I suspect some of the logic has gotten lost in the excessive size. Your main snippet appears equivalent to something like this:
chrctr = tolower(chrctr);
if (strchr(chrctr, "aeiou")) {
isE = (chrctr == 'e');
endSylb = !skipSylb;
skipSylb = 1; // May not be you want, but it's what you have.
}
else {
skipSylb = endSylb = 0;
}
Personally, I think trying to count syllables algorithmically is nearly hopeless, but if you really want to, I'd take a look at the steps in the Porter stemmer for some guidance about how to break up English words in a semi-meaningful way. It's intended to strip off suffixes, but I suspect the problems being solved are similar enough that it might provide at least a little inspiration.

Is using a while block to do nothing a bad thing?

I'm currently working through the excercises in 'The C Programming Language'. Here's one of my solutions:
int c;
while ((c=getchar()) != EOF) {
if (c == ' ') {
while ((c = getchar()) == ' ')
{} // do nothing?
putchar(' ');
}
putchar(c);
}
I found some solutions here that are quite different to mine and use an extra variable to keep track of what's going on, whereas I just use a while loop to skip through all the spaces. My solution feels a bit messy, as it seems bit hackish to have a while loop with nothing between the curly braces. I was wondering if there are any good reasons not to do this? Thanks for any advice :-)
Not at all - I believe you'll find do-nothing loops like these in K&R, so that's about as official as it gets.
It's a matter of personal preference, but I prefer my do-nothing loops like this:
while(something());
Others prefer the semicolon to go on a separate line, to reinforce the fact that it's a loop:
while(something())
;
Still others prefer to use the brackets with nothing inside, as you have done:
while(something())
{
}
It's all valid - you'll just have to pick the style you like and stick with it.
I think it is perfectly acceptable.
I would either write it:
//skip all spaces
while ((c = getchar()) == ' ') {}
to make it obvious that this one line of code does one thing.
Or I would write it like this:
while ((c = getchar()) == ' ') {
//no processing required for spaces
}
so that it matches the rest of your code's format.
Personally, I am not a fan of the
while ((c = getchar()) == ' ');
format. I think it is to easy to overlook the semi-colon.
Your question "Is using a while block to do nothing a bad thing?" may also be answered in terms of wasting CPU cycles. In this case the answer is "No", since, the process will sleep while it waits for the user to input a character.
The process will wake only after a character is input. Then the test will occur and if the test passes, i.e. c == ' ', the process will go to sleep again until a the next character is entered. This repeats until a non-space character is entered.
Well if you really don't like the empty braces, you could refactor that inner loop into
while (c == ' ') {c = getchar();}
This costs one extra comparison though, so a do while loop would be better.
A while that does nothing probably is a bad thing:
while(!ready) {
/* Wait for some other thread to set ready */
}
... is a really, really, expensive way to wait -- it'll use as much CPU as the OS will give it, for as long as ready is false, stealing CPU time with which the other thread could be doing useful work.
However your loop is not doing nothing:
while ((c = getchar()) == ' ')
{}; // skip
... because it is calling getchar() on every iteration. Hence, as everyone else has agreed, what you've done is fine.
I don't think the procedure is, but your formatting is pretty weird. There's nothing wrong with:
/* Eat spaces */
while ((c = getchar()) == ' ');
(that is, indicate there's intentionally not a body)
The canonical way — used since time immemorial, have a look, eg, at the Lyons book — is
while(condition) // Here's the whole thing
; // empty body.
In fact, in general the 'semicolor on a separate line' convention is used for a null statement. You will, for example, occassionally see
if( condition-1)
;
else if (condition-2)
stmt;
else {
// do stuff here
}
It's a lot more uncommon, but shows up either where condition-1 is very complicated, so you don't want to negate it and chance confusion, or where the code has been hand-optimized within an inch of its life, so that you want the most common case first.
The
while(condition) ;
form is to be slavishly avoided, because that's a common and annoying typo: you should make it clear that you did it on purpose. Empty braces
while(condition){
}
or its variants, are also trouble because they either don't stand out enough, or worse lead to other typos.
I would favor:
while ((c = getchar()) == ' ') /* Eat spaces */;
I've also been known to have a procedure named DoNothing specifically for calling in cases like this. It makes it very clear that you really mean to do nothing.
While non-existent loop bodies are perfectly acceptable it should be VERY clear that it's intentional.
I've used code like this. I don't think there's really any reason not to use it if the situation warrants it.
I thinks no problem in it. You can use it, In many situations i prefer it.
Well, not really but it depends on your architecture.
if (dosomething()) { ; }
The above is going to constantly be pushing and popping from your local stack, which has a memory overhead. Also, you will also be flushing your processors' pipelines with noop operations.
An alternative option which hasn't been mentioned yet:
while(condition)
(void)0;
I really do not prefer to write my loops this way, but I had a TA last semester who did.

Resources