Seriously struggling with C

P

pete

Ed said:
How about this? :)

while (c = getchar(), c != EOF) { putchar(c); }

I meant this as a joke, but the more I look at it, the more I like it!

I'm not fond of complicated expressions.

c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}

or

for (c = getchar(); c != EOF; c = getchar()) {
putchar(c);
}
 
A

August Karlstrom

Richard said:
so you disagree with the most famous c line in history?

while(*dest++=*src++);

That is just horrible. A classical example of programming language abuse.
And, as such, can be overkill for simple solutions. What can be easier
to read than

while(numThingsToDo--)

????

while (numThingsToDo > 0) {
...
numThingsToDo--;
}

or even better

for (k = 1; k <= numThingsToDo; k++) { ... }

(assuming that numThingsToDo is "the number of things to do").
There is nothing big and clever by using "for" to obfuscate your code
where the power of the "for" loop is not required.

Agreed.


August
 
R

Richard G. Riley

That is just horrible. A classical example of programming language abuse.

Rubbish.


while (numThingsToDo > 0) {
...
numThingsToDo--;
}

Horrible. Are you assuming all C programmers are idiots?
or even better

for (k = 1; k <= numThingsToDo; k++) { ... }

(assuming that numThingsToDo is "the number of things to do").

That is awful and far more complciated to read and maintain IMO.

Maybe its a style thing :)
 
R

Richard G. Riley

I'm not fond of complicated expressions.

c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}

or

for (c = getchar(); c != EOF; c = getchar()) {
putchar(c);
}

or

while((c=getchar())!=EOF)
putchar(c);

concise and easy to read for a C programmer.
 
C

CBFalconer

Ed said:
.... snip ...

How about this? :)

while (c = getchar(), c != EOF) { putchar(c); }

I meant this as a joke, but the more I look at it, the more I like it!

The problem is that it is understandable ONLY to moderately
experienced C programmers. The very idea of a comma operator is
totally foreign to most, which makes the whole thing obscure.

I wonder where the idea of a comma operator arose. Maybe dmr can
enlighten us. AFAICT its primary use is in the creation of
obfuscative macros.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 
C

CBFalconer

pete said:
I'm not fond of complicated expressions.

c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}

or

for (c = getchar(); c != EOF; c = getchar()) {
putchar(c);
}

I wouldn't even consider anything other than the one liner:

while (EOF != (c = getchar())) putchar(c);

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 
A

August Karlstrom

Richard said:

Then what's your "definition" of programming language abuse? ;-)
Horrible. Are you assuming all C programmers are idiots?

No, if anything, they are too "clever". Keep it simple and explicit. Say
what you mean and mean what you say. It's much easier to reason about
the correctness of a program if multiple side-effects and side-effects
in expressions are avoided.

Example:

while (k > 0) {
...
}
/* Here we know that k <= 0 just by looking at the guard `k > 0'.*/

With side-effects in the "wrong" places you have to decode the meaning
in your head in an unnatural way:

`while (numThingsToDo--)' translates to something like "while
numThingsToDo is not equal to zero before we increment it and then we
increment it and do..." (phew!) There is something inherently ugly about
it and I consider it poor communication.

`while (numThingsToDo > 0)' translates naturally to, well, the obvious.
That is awful and far more complciated to read and maintain IMO.

Well, you are actually right. As the program runs the numThingsToDo
decreases, so it makes sense to decrement it. On the other hand, if we
for instance have a variable named `size' it would be unnatural to use
it as a loop counter if the size it represents doesn't change:

while (size > 0) {
...
size--;
}


August
 
R

Richard G. Riley

Then what's your "definition" of programming language abuse? ;-)

Actually re-reading I didn't quite mean "rubbish" to be so rude :)

while(*d++=*s++) is perfectly sound C : it is good C. It encapsulates
C IMO. How do you term it abuse? You did the accusing so you do the
explanations :)

No, if anything, they are too "clever". Keep it simple and

This is simple and explicit. if you can not understand

while(x--)

then you have no business programming in C. If anything your example
convolutes the problem by adding more lines and comparisons. Of course
we can argue this as a style issue but no programmer worth his salt in
my, not inconsiderable, experience would extend such a trivial C line so.
what you mean and mean what you say. It's much easier to reason about
the correctness of a program if multiple side-effects and side-effects
in expressions are avoided.

Except there are none.

while(x--) could not be simpler.
Example:

while (k > 0) {
...
}
/* Here we know that k <= 0 just by looking at the guard `k > 0'.*/

This is C, not C++.

while(x--) is perfectly readable and legitimate.
With side-effects in the "wrong" places you have to decode the meaning
in your head in an unnatural way:

`while (numThingsToDo--)' translates to something like "while
numThingsToDo is not equal to zero before we increment it and then we
increment it and do..." (phew!) There is something inherently ugly about
it and I consider it poor communication.

Most C programmers would see this immediately as "while there are
things to do". it is a bedrock example of C programming. No
embellishment or cleverness required.
`while (numThingsToDo > 0)' translates naturally to, well, the obvious.


Well, you are actually right. As the program runs the numThingsToDo
decreases, so it makes sense to decrement it. On the other hand, if we
for instance have a variable named `size' it would be unnatural to use
it as a loop counter if the size it represents doesn't change:

while (size > 0) {
...
size--;
}

This is a strawman. We are not talking about size : this would be a
nomenclature thing, not a style thing.

There is another reason for doing the decrement in the loop condition
: it is not requried elsewhere in a potentially complicated area of
code within the loop - for all intents and purposes the counter is
"const" inside the loop and program flow doesnt have to worry about
doing the decrement. e.g Imagine a "break" or a "continue" or
whatever. has the variable been decremented?


To sum up : Let the loop control look after the loop counters : it is
compact, easy to maintain and basic good programming practice.


richard.
 
R

Richard G. Riley

I wouldn't even consider anything other than the one liner:

while (EOF != (c = getchar())) putchar(c);

There is a very good reason why this should never be done.

Debuggers. It would be very hard to step into "putchar". Hence the bog
stand of

while((c=getchar())!=EOF)
putchar(c);

As someone who once had to spend an entire week breaking statement
lines down so that it would be "debugger friendly" I could never
recommend your solution.
 
R

Richard Tobin

The problem is that it is understandable ONLY to moderately
experienced C programmers. The very idea of a comma operator is
totally foreign to most, which makes the whole thing obscure.

I wonder where the idea of a comma operator arose. Maybe dmr can
enlighten us. AFAICT its primary use is in the creation of
obfuscative macros.

Apart from macros, the main use I have made of the comma operator is
in for loops such as

for(i=0, j=0; i<n; i++, j+=3)
...

In this case the combination of comma and semicolon reads quite naturally.

-- Richard
 
R

Richard G. Riley

The problem is that it is understandable ONLY to moderately
experienced C programmers. The very idea of a comma operator is
totally foreign to most, which makes the whole thing obscure.

I wonder where the idea of a comma operator arose. Maybe dmr can
enlighten us. AFAICT its primary use is in the creation of
obfuscative macros.

It is very common in (of course) variable declarations and also
declarations and assignments in the initialising clause of for loops

e.g

for(i=0,j=2;check(i,j);i++,j++);
 
B

Ben Pfaff

[about the comma operator]
It is very common in (of course) variable declarations [...]

No it isn't. The commas in
int a, b, c;
are not operators.
and also declarations and assignments in the initialising
clause of for loops

In an assignment, yes; in a declaration, no.
 
R

Richard G. Riley

[about the comma operator]
It is very common in (of course) variable declarations [...]

No it isn't. The commas in
int a, b, c;
are not operators.
and also declarations and assignments in the initialising
clause of for loops

In an assignment, yes; in a declaration, no.

I stand corrected I guess I meant to state where commas are used "in general".
 
P

pete

Richard said:
This is simple and explicit. if you can not understand

while(x--)

then you have no business programming in C.

I always use an explicit comparison like (x-- != 0),
unless the nature of the expression is at least quasiboolean, like
while (isspace(x))

But, I use
while (n-- != 0)
frequently.

http://groups.google.com/group/comp.lang.c/msg/c0103a58a6d6e4e0

(n-- != 0) is exactly perfect for work
from the high end to the low end of an array.

void *mem_cpy(void *s1, const void *s2, size_t n)
{
unsigned char *p1 = s1;
const unsigned char *p2 = s2;

while (n-- != 0) {
p1[n] = p2[n];
}
return s1;
}
 
R

Richard G. Riley

Richard said:
This is simple and explicit. if you can not understand

while(x--)

then you have no business programming in C.

I always use an explicit comparison like (x-- != 0),
unless the nature of the expression is at least quasiboolean, like
while (isspace(x))

But, I use
while (n-- != 0)
frequently.

http://groups.google.com/group/comp.lang.c/msg/c0103a58a6d6e4e0

(n-- != 0) is exactly perfect for work
from the high end to the low end of an array.

void *mem_cpy(void *s1, const void *s2, size_t n)
{
unsigned char *p1 = s1;
const unsigned char *p2 = s2;

while (n-- != 0) {
p1[n] = p2[n];
}
return s1;
}

There seems to be a trend towards array indexing for basic incremental
memory accesses. Is there a reason for that? Any other use of "n"
might put the compiler off optimiszing and end up in far slower
code. With this technique there is no room for potential non
optimization -



while(n--)
*p1++=*p2++;


(with p2 obviously not being a const)
 
C

Chris Torek

while (n-- != 0) {
p1[n] = p2[n];

There seems to be a trend towards array indexing for basic incremental
memory accesses. Is there a reason for that? Any other use of "n"
might put the compiler off optimiszing and end up in far slower
code. With this technique there is no room for potential non
optimization -

while(n--)
*p1++=*p2++;

(with p2 obviously not being a const)

Straightforward translation of Pete's loop on the SPARC yields:

# n in %o2, p1 in %o0, p2 in %o1
Lloop:
tst %o2 # set %icc based on n==0
bz Lend_loop # the next instruction executes even if we branch
dec %o2 # decrement n
ldub %l0,[%o0+%o2] # fetch byte p1[n]
b Lloop # the next instruction still executes
stub %l0,[%o1+%o2] # store byte at p2[n]
Lend_loop:

Straightforward translation of your loop yields:

Lloop:
tst %o2 # set %icc based on n==0
bz Lend_loop # the next instruction executes even if we branch
dec %o2 # decrement n
ldub %l0,[%o0] # fetch byte *p1
inc %o0 # increment p1
stub %l0,[%o1] # store byte at *p2
b Lloop # the next instruction still executes
inc %o1 # increment p2
Lend_loop:

Your loop contains 8 instructions, versus 6 for Pete's.

(Pete's loop does have the drawback of running "backwards" through
memory, which is sometimes slower in filling cache misses depending
on the memory subsystem.)
 
B

Ben Pfaff

Richard G. Riley said:
There seems to be a trend towards array indexing for basic incremental
memory accesses. Is there a reason for that?

I like to write my code to be as clear as possible. I only worry
about micro-optimizations, like *p++ versus p[n], when I've
demonstrated that the function in question shows up as important
on a profiling run.

Whether *p++ or p[n] is clearer depends on the situation and how
I'm feeling that day.
 
D

dave

Richard G. Riley said:
There seems to be a trend towards array indexing for basic incremental
memory accesses. Is there a reason for that?

I like to write my code to be as clear as possible. I only worry
about micro-optimizations, like *p++ versus p[n], when I've
demonstrated that the function in question shows up as important
on a profiling run.

Considering Chris Torek's reply, it appears p[n] is the optimal form on at
least one compiler. I thought that *p++ not being an _automatic_ choice
for optimization had been known for years.


~Dave~
 
C

CBFalconer

Richard G. Riley said:
.... snip ...

There is a very good reason why this should never be done.

Debuggers. It would be very hard to step into "putchar". Hence
the bog stand of

while((c=getchar())!=EOF)
putchar(c);

As someone who once had to spend an entire week breaking statement
lines down so that it would be "debugger friendly" I could never
recommend your solution.

Yet you are willing to eliminate the clarifying blanks in the
statement. At the same time I cannot remember when I last used a
debugger in anger. printf usually is adequate.

At any rate we all have our reasons for our preferances.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 
C

CBFalconer

Chris said:
Straightforward translation of Pete's loop on the SPARC yields:

Purely as an observation, I find that the gcc x86 code generator
tends to partially unroll while loops. i.e.:

while (NUM != x) {
dothings();
}

tends to become:

if (NUM != x)
do {
dothings()
} while (NUM != x);

this cuts the instructions executed in the inner loop, but slightly
inflates the overall code, and normally speeds things up. I have
not observed the variation with optimization setting.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top