tanikaze
tanikaze

Reputation: 70

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?

Upvotes: 0

Views: 1466

Answers (3)

user3629249
user3629249

Reputation: 16540

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

Upvotes: 0

rici
rici

Reputation: 241671

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) && ....

Upvotes: 1

user3920237
user3920237

Reputation:

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

Upvotes: 0

Related Questions