Kernighan and Ritchie Exercise 2-2 Debugging? - c

I am working through K&R (2nd edition) for my own edification and encountered the following exercise (exercise 2-2 p42):
Write a loop equivalent to the following without using && or ||:
for (i=0; i<lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
s[i] = c;
This was my solution:
#include <stdio.h>
/* write a loop equivalent to the following without using && or ||
for (i=0; i<lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
s[i] = c;
*/
int main()
{
int counter = 0, lim = 1000;
int s[lim], c;
while(counter < lim-1)
{
while((c = getchar()) != '\n')
{
while(c != EOF)
{
s[counter] = c;
}
}
counter++;
}
return 0;
}
I was expecting the indented loops and therefore the entire program to exit normally once it encountered a newline character ('\n') or an EOF character (Ctrl-d on my Linux machine), but to my surprise it happily soldiers on. I tried to debug it using gdb but still could not figure it out.
What am I not seeing?
Addendum: I tried to reverse the sequence of tests the while loops perform and added an if statement to break out of the outer loop if c == '\n' but am still not seeing it! I am also having difficulty trying to run GDB entering text into the command line and simultaneously printing the value of c, even when I tried to link gdb to the pid of a running copy of the executable. I realize that there are probably other ways to solve this exercise, e.g. setting an OK_TO_EXECUTE flag or variable that is true only if all three conditions are met, but I am bothered by the fact that I seem unable to find the bug in a seemingly simple program. This is precisely why I am returning to K&R to go through the book more thoroughly and to solve the exercises properly.
Redone code (still buggy!!!):
#include <stdio.h>
/* write a loop equivalent to the following without using && or ||
for (i=0; i<lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
s[i] = c;
*/
int main()
{
int counter = 0, lim = 1000;
int s[lim], c;
while((c = getchar()) != EOF)
{
if ( c == '\n')
break;
while(c != '\n')
{
while(counter < lim-1)
{
s[counter] = c;
counter++;
}
}
}
return 0;
}
SOLVED! - I think! I think I have finally figured it out. The inner loops as written in my redone solution will still loop endlessly or at lease till lim is reached. I added break
statements and think I am on my way to a solution.
I am still wrestling with how to run gdb on this problem though; enter the command line entries AND print the value of c. Linking gdb to the pid of the executable still did not work as expected. I even posted a separate question regarding gdb.

but to my surprise it happily soldiers on
You have three nested loops. A newline would terminate one of the inner loops, while the outermost loop would happily carry on (until you've hit Enter lim times).
I can give you a hint: you probably shouldn't be using nested loops for this.

You have added loops that didn't exist in the original ... that's conceptually and logically wrong. The most obvious solution uses break:
for (i = 0; i < lim-1; ++i)
{
c = getchar();
if (c == '\n')
break;
if (c == EOF)
break;
s[i] = c;
}
Or if you're pretending that C doesn't have break, you can do something like this (this is not exactly equivalent because i doesn't have the same value if '\n' or EOF is encountered):
for (i = 0; i < lim-1;)
{
c = getchar();
if (c == '\n')
i = lim-1;
else if (c == EOF)
i = lim-1;
else
s[i++] = c;
}
Or you can use the Pascal approach:
#include <stdbool.h>
...
i = 0;
bool more = i < lim-1;
while (more)
{
c = getchar();
if (c == '\n')
more = false;
else if (c == EOF)
more = false;
else
{
s[i++] = c;
more = i < lim-1;
}
}

With goto
i=0;
loop:
if( i >= lim - 1) goto end;
c = getchar();
if(c == '\n') goto end;
if(c == EOF) goto end;
s[i++] = c;
goto loop;
end:
Without break, goto and with just one for, still without && and ||.
for (i=0; i < lim - 1 ? ((c=getchar()) == '\n' | c == EOF) == 0 : 0; ++i)
s[i] = c;
Update
As noted by #Jim it's way better to set order of execution explicitly by using internal ?:
for (i=0; i >= lim - 1 ? 0 : (c=getchar()) == '\n' ? 0 : c != EOF; ++i)
s[i] = c;

Related

is there any way to stop a (c = getchar()) != EOF) if my work inside the while loop is done?

I am reading the C programming language book Dennis M. Ritchie and
trying to solve this question:
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.
I think my solution works, but the problem is that if I don't press EOF, the terminal won't show the
result. I know that the condition specifies that exactly, but I am
wondering whether there is any way to make the program terminate after
reading a single line? (Sorry if my explanation of the problem is a bit shallow. Feel free to ask more.)
#include <stdio.h>
int main ()
{
int digits[10];
int nc=0;
int c, i, j;
for (i = 0; i <= 10; i++)
digits[i] = 0;
//take input;
while ((c = getchar ()) != EOF) {
++nc;
if (c == ' ' || c=='\n') {
++digits[nc-1];
//is it also counting the space in nc? i think it is,so we should do nc-1
nc = 0;
}
}
for (i = 1; i <= 5; i++) {
printf("%d :", i);
for (j = 1; j <= digits[i]; j++) {
printf ("*");
}
printf ("\n");
}
// I think this is a problem with getchar()
//the program doesn't exit automatically
//need to find a way to do it
}
You could try to make something like
while ((c = getchar ()) != EOF && c != '\n') {
and then adding a line after the while loop to account for the last word:
if (c == '\n') {
++digits[nc-1];
nc = 0;
There is also another problem inside your program. ++digits[nc-1]; is correct, however, for the wrong reason. You should make it because an array starts at zero, i.e. if you have an array of length 10, it will go from 0 to 9, so you should count the length of the words and then add one to the position of the array length - 1 (as there are no words of length zero). The problem is that you are still counting the blank spaces or the newline characters inside the length of a word, so if you have two blank spaces after a word of length 4, the program will add to the array a word of length 5 + a word of length 1. To avoid this, you should do something like this:
while ((c = getchar ()) != EOF) {
if ((c == ' ' || c == '\n' || c == '\t') && nc > 0) {
++digits[nc-1]; // arrays start at zero
nc = 0;
}
else {
++nc;
}
}

Function to read line from standard input in C not working as expected

This function is supposed to get a line from the terminal. But it doesn't! I have gone over the code multiple times, but I haven't been able to pinpoint the problem! Please help! It doesn't seem that the code is entering the while block.
int getline(char line[]) {
int i = 0 ;
int c ;
while( ((c=getchar()) != EOF) && (c =! '\n') ) {
line[i++] = c ;
}
line[i] = '\0' ;
return i ;
}
Well this is incorrect
while( ((c=getchar()) != EOF) && (c =! '\n') )
it should be
while( ((c=getchar()) != EOF) && (c != '\n') )
Do you notice the difference? != is comparison (which is correct), and =! is completely different (which means negating '\n' and assign it to c) - which was wrong. So, attentions to the details please :)

K&R The C Programming Language - Exercise 2-2: Check my work?

I'm not exactly struggling with this exercise(holy crap, after 1-20 through 1-24...), but I am wondering if there is something wrong with my solution. The exercise asks you to write a loop equivalent to the following:
for (i=0; i<lim-1 && (c=getchar()) != '\n' && c!= EOF; ++i)
s[i] = c;
without the use of || or &&. I am choosing not to use any language features not introduced to me already, and I am taking seriously the idea that each exercise is placed where it is because the intent is for the user to use the supplied concepts. Thus, I came up with this:
for (i=0; (i<lim-1) == ((c=getchar()) != EOF) == (c != '\n'); ++i)
s[i] = c;
There's a much prettier solution involving a simple if/else if type conditional, but I wanted to make use of this section's talk about expressions having numerical values based on their truth or falseness and so on. The thing is, my solution isn't in any of the sites I normally check for valid exercise solutions, so even though it appears to work the same. I was wondering if there's anything wrong with the way I did it, other than it being ugly? Does the functionality differ in some way?
Aside from anything else, it is undefined behaviour because it depends on evaluation order. && and || definitely evaluate their left operands first, but == doesn't. So there is no guarantee that the second use of c will refer to the new value rather than the value from the previous loop (or an unitialised value the first time.)
The short-circuit evaluation of && is an important part of the semantics of the original loop. The fact that && doesn't evaluate its second operand if its first operand is false means that the original loop is guaranteed to call getchar no more than lim-1 times. Rewritten without &&, getchar will be called an additional time when i reaches lim-1, and the character read (if there was one) is then dropped into the bit bucket, which means that it will be missing from the next attempt to read input.
Finally, a == b is not the same as a && b. It works for (c != EOF) == (c != '\n') because it is not possible for both of those conditions to be false -- which is an interesting observation -- but it is not a replacement for (i < lim-1) && ....
// with the use of break statement and no else statements
for( i=0; i<(lim-1); ++i )
{
c = getchar();
if( '\n' == c ) break;
if( EOF == c ) break;
s[i] = c;
}
Your code likely has undefined behavior. Compiling it under GCC and Clang gives me similar warnings:
test.cpp:14:31: warning: unsequenced modification and access to 'c'
[-Wunsequenced]
for (i=0; (i<lim-1) == ((c=getchar()) != EOF) == (c != '\n'); ++i)
Reading sequence points in c and c-faq question 3.8 I'm not convinced == is a sequence point, but && is. Secondly, I don't think your code does what you think it does. If we take the following example:
#include <stdio.h>
int main()
{
int lim = 512;
int a = 1;
char b = EOF;
char c = '\n';
if ((a < lim) == (b != EOF) == (c != '\n'))
{
printf("True.");
} else {
printf("False.");
}
}
It outputs "True." So it is in fact wrong. I recommend trying to avoid writing clever code and instead stick to the "if/else" solution that you alluded to.
while(i < lim-1) {
c = getchar();
if (c == '\n')
lim = 0; /* We haven't encountered breaks yet. */
else if (c == EOF)
lim = 0;
else
s[i++] = c;
}

Can't assign value to a variable inside a for loop

Here is what I want to do:
Read all characters from a '.c' file and store that into an array.
When a character from that array is '{', it will be pushed into a stack. And count of pushed characters will be increased by 1.
When a character from that array is '}', stack will pop and the count of popped characters will be increased by 1.
Compare those two counts to check whether there is a missing '{' or '}'
Here is my code:
int getLinesSyntax(char s[], int limit, FILE *cfile)
{
int i, c, push_count = 0, pop_count = 0;
int state = CODE;
int brackets[limit];
char braces[limit];
for(i = 0; i < 100; i++)
{
braces[i] = 0;
}
for(i = 0; i < limit - 1 && (c = getc(cfile)) != EOF && c != '\n'; i++)
{
s[i] = c;
if(s[i] == '{')
{
braces[0] = s[i];
//push(s[i], braces);
++push_count;
}
else if(s[i] == '}')
{
pop(braces);
++pop_count;
}
}
//Mor shiljih uyed array -n togsgold 0-g zalgana
if(c == '\n')
{
s[i] = c;
i++;
}
s[i] = '\0';
i = i -1; //Suuld zalgasan 0 -g toonoos hasna
if(c == EOF)
{
//just checking
for(i = 0; i < 100; i++)
{
printf("%d", braces[i]);
}
if(push_count != pop_count)
{
printf("%d and %d syntax error: braces", push_count, pop_count);
}
return -1;
}
else
{
return i;
}
}
Here is the output
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
The problems is:
Assignments inside the for loop is not working. (It's working when I put that outside of the loop)
I would like to know if there's something wrong with my code :).
There are several problems.
Lets go through it step by step
1) Your array initialization loop:
int brackets[limit];
char braces[limit];
for(i = 0; i < 100; i++)
{
braces[i] = 0;
}
You declare the array having size of limit but only initialize 100 items. Change 100 to limit to fully initialize it depending on the parameter of the function.
2) The conditional statement of the main for loop:
i < limit - 1 && (c = getc(cfile)) != EOF && c != '\n'
Although the first substatement is correct I have two remarks:
Firstly (c = getc(cfile)) != EOF might be one reason why the loop is never accessed and still everything is 000000.... Check if the file exists, the pointer is not NULL or other silent errors occured.
Secondly the c != '\n'. What if one of these characters occurs? In this case you won't continue with the next iteration but break out of the entire forloop. Remove it there and put it in the first line of the body like this:
if(c == '\n')
{
i -= 1; // to really skip the character and maintain the index.
continue;
}
3) s[i] = c;
Can you be certain, that the array is indeed sizeof limit?
4) Checking for curly braces
if(s[i] == '{')
{
braces[0] = s[i];
//push(s[i], braces);
++push_count;
}
else if(s[i] == '}')
{
pop(braces);
++pop_count;
}
You assign to braces[0] always, why?
5) Uninitialized access
if(c == '\n')
{
s[i] = c;
i++;
}
s[i] = '\0';
i = i -1; //Suuld zalgasan 0 -g toonoos hasna
You're now using the function-global variable i, which is never initialized properly for this block. What you do is to use a variable that is used basically everywhere ( which is basically also no problem from the memory point of view. ), but you rely on legacy values. Is this done by purpose? If no, reinitialize i properly. I have to ask this since i can't read your comments in code.
What I'm quite unhappy about is that you entirely rely on one variable in all the loops and statements. Usually a loop-index should never be altered from inside. Maybe you can come up with a cleaner design of the function like an additional index variable you parallelly increase without altering i. The additional index will be used for array access where appropriate whereas i really remains just a counter.
I think the problem is in this condition "c != '\n'" which is breaking the for loop right after the first line, before it reaches any brackets. And hence the output.
For the task of counting whether there are balanced braces in the data, the code is excessively complex. You could simply use:
int l_brace = 0;
int r_brace = 0;
int c;
while ((c = getchar()) != EOF)
{
if (c == '{')
l_brace++;
else if (c == '}')
r_brace++;
}
if (l_brace != r_brace)
printf("Number of { = %d; number of } = %d\n", l_brace, r_brace);
Of course, this can be confused by code such as:
/* This is a comment with an { in it */
char string[] = "{{{";
char c = '{';
There are no braces that mark control-of-flow statement grouping in that fragment, for all there are 5 left braces ({) in the source code. Parsing C properly is hard work.

a c programming question

all. I am not sure whether it is appropriate to ask such a 'simple' question here, but actually it's hard for me :[ , and here is the question and a bit of c code:
main()
{
int c, i;
for (i = 0; (c = getchar()) != EOF && c != '\n'; ++i)
printf("%d", i);
if (c == '\n')
printf("%d", i);
}
After executing this program, when I input, say, "abc\n", the program will return:
0
1
2
3
I wondered why the result is not
0
1
2
since when c == '\n', there are no statement that increments i by 1. This is what I thought, I must be wrong, would you tell me where I was wrong? Thanks!
The sequence of operations in the for loop are:
i = 0
(c = getchar()) != EOF && c != '\n' // c is set to 'a'
printf("%d", i) // displays 0
++i // i == 1
(c = getchar()) != EOF && c != '\n' // c is set to 'b'
printf("%d", i) // displays 1
++i // i == 2
(c = getchar()) != EOF && c != '\n' // c is set to 'c'
printf("%d", i) // displays 2
++i // i == 3
(c = getchar()) != EOF && c != '\n' // c is set to '\n'
// the loop exits
So the printf() that's after the for loop prints the most recent value for i, which is 3.
The ++i gets executed after the c == '\n' case.
Perhaps this code would help clarify?
int i;
for (i = 0; i <= 3; ++i)
printf("%d\n", i);
At the end of the loop, i will be 4, because of that final increment.
The main problem is with pre increment of index variable i. In stead of pre increment, use post increment i.e. i++ inside the for loop.The reason behind that is due to pre increment. When the condition inside the loop halts, the value stored in i is already 4 when you use pre increment.
main()
{
int c, i;
for (i = 0; (c = getchar()) != EOF && c != '\n'; i++)
printf("%d", i);
if (c == '\n')
printf("%d", i);
}

Resources