What a stupid gcc!

I

Ian Collins

Because it won't work?

Exactly the same 'i' could be in use simultaneously in different functions.

That's unlikely to be the case with a single local 'i' in a function.

But you can also go too far the other way, and end up with this (modified
from Noob's example):

for (int i = 0; i< N1; ++i)
for (int i = 0; i< N2; ++i)
for (int i = 0; i< N3; ++i)
do_whatever_with(i, i, i); /* ? */

Why would anyone do something so foolish?
 
P

Paul J Gans

What has strong typing do do with where a variable is declared?

Historically? Back when computers were slow and compilations
took forever, one wanted a one-pass compiler. To do that (even
if there was a post-compilation "fix-up" cycle) one had to
have things declared prior to use.

Today that isn't so important, though programmers have their
preferences.

Speaking for myself I have no objection to block-level declaration
of temporary variables such as i or j. However giving them
names that look like important variables in a routine can be
quite confusing, especially if you did not write the code and
are only maintaining it.
 
P

Paul J Gans

Ben Bacarisse said:
I've snipped all the context because I don't think you address the issue
where variables are declared. But I do want to comment...
Cobol was massive long before C came along, and Algol dates from the
1950s. Basic is a little younger than Algol, but it was around in the
1960s and was riding a wave caused by the boom in microprocessors
(admittedly in a dizzying array of dialects) when most people started to
became aware of C.
I would not say that C has strong typing. It added typing to its
predecessor (B) but it did so in such a way that the types rules are
deliberately easy to break or circumvent.

True. I've always thought of that as a "fall out" of being
"close to the hardware", whatever that means.
 
G

Guest

That means looking at every line of code before that point, for a variable
name that be similar to many others. Declarations at the start of a function
are easier for low-tech editors (even with one that can't switch quickly
between two locations, probably you can run two instances of it).

low tech?! what are you using SOS?
 
G

Guest

On Monday, June 18, 2012 10:37:04 PM UTC+1, Ian Collins wrote:

C is the only language I know that had (in the last century) such
restrictive declaration rules.

[declarations only at the beginning of a block]

You wouldn't see this argument on a
forum for another language. If old C's declaration rules were such a
good idea, why have thy been ignored by most (all?) subsequent languages?

Alogol-60 (hence CORAL-66 and JOVIAL), Pascal

I've never found it a restriction. Though I happily declare stuff mid-block in C++.
 
I

Ike Naar

Because it won't work?

Exactly the same 'i' could be in use simultaneously in different functions.

That's unlikely to be the case with a single local 'i' in a function.

But you can also go too far the other way, and end up with this (modified
from Noob's example):

for (int i = 0; i < N1; ++i)
for (int i = 0; i < N2; ++i)
for (int i = 0; i < N3; ++i)
do_whatever_with(i, i, i); /* ? */

Yes, that's gone too far the other way and it won't work.
In the inner loop the outer i's are hidden by the inner i.

But this is not a very good argument against declaring variables
close to where they are used. If you would declare i at the start
of the function, it would be a mess as well:

int i;
/* ... */
for (i = 0; i < N1; ++i)
for (i = 0; i < N2; ++i)
for (i = 0; i < N3; ++i)
do_whatever_with(i, i, i); /* ? */

The mess has nothing to do with the placement of the declarations,
it's a mess because, in one context, one identifier is used for
three purposes.

In

int i, j, k;
/* ... */
for (i = 0; i < N1; ++i)
for (j = 0; j < N2; ++j)
for (k = 0; k < N3; ++k)
do_whatever_with(i, j, k);

vs.

for (int i = 0; i < N1; ++i)
for (int j = 0; j < N2; ++j)
for (int k = 0; k < N3; ++k)
do_whatever_with(i, j, k);

I don't see why the first variant would be an improvement
over the second one.
 
B

BartC

In

int i, j, k;
/* ... */
for (i = 0; i < N1; ++i)
for (j = 0; j < N2; ++j)
for (k = 0; k < N3; ++k)
do_whatever_with(i, j, k);

vs.

for (int i = 0; i < N1; ++i)
for (int j = 0; j < N2; ++j)
for (int k = 0; k < N3; ++k)
do_whatever_with(i, j, k);

I don't see why the first variant would be an improvement
over the second one.

(1) Imagine this 3-loop construction occurs half-a-dozen times in the
function

(2) You would only need one 'int' declaration in the first variant, rather
than 18.

(3) The extra clutter in the second, is only telling you what you've already
guessed, that these loop indices are all ints.

(4) But if you *did*, at some point, need to change the loop indices from
int to something else, you've got eighteen times as much editing to do.

(5) This would be bad practice, but if you ever found it necessary to goto
from one loop body to another, the loop indices would be undefined.

(6) If you wanted to break out of any of the loops, and needed to refer to
the value of the loop variable outside, it would either not exist, or be
confused with a more global version.

(7) If ever needed to refer to one of the variables, either in a debugger,
or on the phone to somebody, or in a note or email or comment, or even in a
printf caption, then you've got a problem.

Admittedly for the single bit of code in your example, it's not much of an
issue. But for the more general case of having declarations scattered
everywhere, identically-named variables shadowing each other in nested
scopes, identically-named variables from different scopes getting mixed up
(and what *does* happen when you goto from from block to another?), then
I've already pointed out some problems elsewhere.
 
B

BartC

low tech?! what are you using SOS?

SOS (if you're talking about the old DEC editor) wasn't so bad for editing
on a paper terminal. (It was sheer luxury compared with one or two others.)

And you could instantly print a hard copy of your function (actually you had
to), and could use that for reference.

But no that's not what I use now.

You can use smart editors, but that can be taken to extremes. It is
desirable for the source code of a language to be stored in a simple,
accessible format, not dependent on clever interpretive tools. For example,
can you print off a copy of a function, direct from a file, and
read it?

Having in-place and block-scope declarations, just crosses that line,
because it can make reading a paper (or dumb) copy more difficult.
 
J

James Kuyper

On 06/19/12 12:30 PM, BartC wrote: ....

Why would anyone do something so foolish?

There's no upper limit on human stupidity. However, I think that only a
badly-written code generation program is likely to produce code
containing that particular error.
 
J

James Kuyper

(1) Imagine this 3-loop construction occurs half-a-dozen times in the
function

(2) You would only need one 'int' declaration in the first variant, rather
than 18.

(3) The extra clutter in the second, is only telling you what you've already
guessed, that these loop indices are all ints.

No, it's also telling you that each loop index will be used, with it's
particular meaning, within a restricted scope.
(4) But if you *did*, at some point, need to change the loop indices from
int to something else, you've got eighteen times as much editing to do.

No, in general, if you change the type of one of the loop indexes, you
will NOT need to change all of the other seventeen. That's the reason
why separating them is both possible and desirable.
(5) This would be bad practice, but if you ever found it necessary to goto
from one loop body to another, the loop indices would be undefined.

Yes, and a diagnostic is mandatory when that occurs. Making that
diagnostic mandatory is one of the purposes of writing code this way. If
you're changing a variable from having a meaning that's restricted to
one limited scope, into a variable with a wider scope, you should move
the point of declaration to reflect that fact. This is not the kind of
change that should be easy to make without thinking about the consequences.
(6) If you wanted to break out of any of the loops, and needed to refer to
the value of the loop variable outside, it would either not exist, or be
confused with a more global version.

As with point 5, requiring you to move the declaration in order to avoid
the otherwise mandatory diagnostic is precisely the point of writing
code this way. You should have to invest at least a little bit of time
thinking about it whenever you decide to widen the scope of a variable.
When you always declare them with the widest possible scope, it does
make things easier, but in particular it makes it easier to commit
design errors, such as using the same variable name for two unrelated
purposes.
(7) If ever needed to refer to one of the variables, either in a debugger,
or on the phone to somebody, or in a note or email or comment, or even in a
printf caption, then you've got a problem.

No, in general that's not a problem; the context will usually make it
clear which variable you're referring to; if you're referring to it out
of the proper context, that's the problem, not the fact that scope has
been deliberately limited. If the proper context is bigger than the
scope a variable has been given, then the scope is too small, and it's
declaration should be moved to the innermost block that contains all of
the contexts where it is needed; but only the innermost one, not to any
block farther out.
Admittedly for the single bit of code in your example, it's not much of an
issue. But for the more general case of having declarations scattered
everywhere, identically-named variables shadowing each other in nested
scopes,

Shadowing is something to be avoided in general; by giving each variable
the smallest scope consistent with its intended use, you actually reduce
the chance that another variable with the same name will shadow it (or,
more seriously, be accidentally merged with it by using a single
declaration for both uses).
identically-named variables from different scopes getting mixed up

Defining variables with the smallest scope consistent with their
intended use prevents mixing them up; declaring them with a wider scope
than they need creates more opportunities for mixing them up.
(and what *does* happen when you goto from from block to another?),

If the second block is not nested inside the first one, then variables
declared in the first block will no longer be visible. If that's a
problem for some particular variable, then that variable has NOT been
declared with the smallest possible scope consistent with its intended
use; it needs to be moved up to a higher scope that includes both
blocks. The point is, it should be moved up only to the smallest scope
that includes both blocks.
 
J

James Kuyper

Historically? Back when computers were slow and compilations
took forever, one wanted a one-pass compiler. To do that (even
if there was a post-compilation "fix-up" cycle) one had to
have things declared prior to use.

That's a separate issue, though a related one. In C, the scope of a
block-scope declaration does not start until it's declarator is
complete, a fact which is related to the issue you raise. However, the
scope ends with the enclosing block, and that's what's relevant to the
issue we're talking about.

What we're talking about would remain an issue even if C were modified
to extend the scope of a declaration to beginning of the enclosing
block. The key issue is the fact that the scope is associated with the
enclosing block, and whether or not it's a good idea to take advantage
of this fact in any block other than the outermost one.
 
J

James Kuyper

On 06/19/2012 06:28 AM, BartC wrote:
....
Having in-place and block-scope declarations, just crosses that line,
because it can make reading a paper (or dumb) copy more difficult.

I find it easier to find declarations when they're declared closer to
the point of use, not harder. Your point about scanning every line is
more relevant to paper print-outs than for files viewed on the computer,
but it also applies, with equal strength (which isn't very much) when
searching for the start of the function. And when the declarations are
found only at the start of the function, and I have to search through a
larger number of lines, on average, to get there.
 
B

Ben Bacarisse

On Monday, June 18, 2012 10:37:04 PM UTC+1, Ian Collins wrote:

C is the only language I know that had (in the last century) such
restrictive declaration rules.

[declarations only at the beginning of a block]

You wouldn't see this argument on a
forum for another language. If old C's declaration rules were such a
good idea, why have thy been ignored by most (all?) subsequent languages?

Alogol-60 (hence CORAL-66 and JOVIAL), Pascal

I'm getting confused by all this. These can't be counter
examples, i.e. subsequent languages that did *not* ignore C's
declaration rules, because they all pre-date C.

<snip>
 
B

Ben Bacarisse

Sometimes I read something on c.l.c that takes my breath away. This
following is one of those cases.
(1) Imagine this 3-loop construction occurs half-a-dozen times in the
function

OK, so you are going to list benefits predicated on having a truly
dreadful bit of code?
(2) You would only need one 'int' declaration in the first variant, rather
than 18.

That's a fact, but not in it self a benefit, unless I missed the "fewer
declarations are always better" rule.
(3) The extra clutter in the second, is only telling you what you've already
guessed, that these loop indices are all ints.

(4) But if you *did*, at some point, need to change the loop indices from
int to something else, you've got eighteen times as much editing to
do.

These two go together. Something is clutter if all it does is confirm a
guess, yet it has the advantage that you can make that guess be wrong in
one simple edit. Surely you'd now prefer all the 18 declarations so as
to preclude an incorrect guess.

I would change a declaration at the top of a function containing such
replicated code only with great trepidation and much checking. Are all
the loops really the same in needing the new type? Are i, j or k used
in any other way such that changing the type will cause a problem? I'd
*much* rather make 18 edits to declarations with the smallest possible
scope so that I can review each one.
(5) This would be bad practice, but if you ever found it necessary to goto
from one loop body to another, the loop indices would be undefined.

This is not a serious suggestion. You can't really want to say that a
single declaration is better because it eases that path of putting in
jumps from one loop body to another. I think you just got carried away.
(6) If you wanted to break out of any of the loops, and needed to refer to
the value of the loop variable outside, it would either not exist, or be
confused with a more global version.

The more breaks there are, the tighter the control I want on the scope
of identifiers. You are right that you need to move the declaration out
of the loop, but that's not an argument that it should (a) be moved to
the top of encoding block, or (b) that it should have been there right
form the start. It's pessimistic programming at its worst to think that
you'll be jumping all over the place and changing the types of loop
index variables so you might as well stick them together at the top
right away.
(7) If ever needed to refer to one of the variables, either in a debugger,
or on the phone to somebody, or in a note or email or comment, or even in a
printf caption, then you've got a problem.

"Hey, you know that really crap function with 6 repeated triple-nested
loops? The bounds for i in the third one are wrong."

I had to joke simply because I don't really understand this "benefit".
Surely with one shared declaration, whatever the problem is, it now
related to a use of the variable rather than the variable itself?
 
S

Stephen Sprunk

Historically? Back when computers were slow and compilations
took forever, one wanted a one-pass compiler. To do that (even
if there was a post-compilation "fix-up" cycle) one had to
have things declared prior to use.

I thought the issue wasn't so much speed but rather that computers of
the day didn't have enough memory to hold a source file entirely in
memory, which would be required for two (or more) passes. Being able to
emit object code as the compiler progressed and then forget it allowed a
compiler to handle arbitrarily large source files.

S
 
J

James Kuyper

On 06/18/2012 09:46 PM, Paul J Gans wrote:
....
Historically? Back when computers were slow and compilations
took forever, one wanted a one-pass compiler. To do that (even
if there was a post-compilation "fix-up" cycle) one had to
have things declared prior to use.

Keep in mind that C's permission for the existence of tentative
definitions interferes with on-pass compilation.
 
B

BartC

Ben Bacarisse said:
OK, so you are going to list benefits predicated on having a truly
dreadful bit of code?

OK, just have two loops then. You can't say a function is terrible because
it's got two separate nested loops.
That's a fact, but not in it self a benefit, unless I missed the "fewer
declarations are always better" rule.

C clearly thinks it's a benefit; why else would the language allow you to
write:

int x,y,z;

instead of:

int x;
int y;
int z;
This is not a serious suggestion. You can't really want to say that a
single declaration is better because it eases that path of putting in
jumps from one loop body to another. I think you just got carried away.

The real issue is to do with entering and leaving different scopes when goto
is used. I can see that that might have similar problems to a goto between
one function and another. With a single scope for the function, that doesn't
happen. Maybe goto shouldn't be used that way, but if it is, then it's
another headache to sort out.
"Hey, you know that really crap function with 6 repeated triple-nested
loops? The bounds for i in the third one are wrong."

I had to joke simply because I don't really understand this "benefit".
Surely with one shared declaration, whatever the problem is, it now
related to a use of the variable rather than the variable itself?

My comment was serious. With a single lexical scope, each local variable has
a well-defined and unique qualifying path. With variables local to a block,
they now become anonymous, and you have the extra problem that each block
may be dynamically active or inactive, but no way of identifying any block.

Although, my argument against this is separate from the in-place
declarations; it would be possible to have those, and still have one lexical
scope for the function. Or perhaps I'm confused; if my understanding of how
it works is correct, the following code is wrong:

void fn(void){
int x=1,y=2;

if (x) {
int z=3;
}
else {
z=4;
}

printf("X,Y,Z = %d %d %d\n", x, y, z);
}

'z' is declared on it's first use, but because that happens inside a pair of
braces, then it's scope is limited to that block.

In that case, the problems are worse than I thought; if someone first
writes:

if (x)
int z=3;

then that's fine; but as soon as an extra statement is needed, and a {,}
block is created, it stops working! It sounds like a nightmare to me, but
obviously people here seem to like it.
 
K

Kaz Kylheku

Why don't you go one step further and declare
retval, temp, i, j, and k as global variables?

In part, for the same reason why you don't keep them block scope local,
but stick "static" on them.
 

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
474,079
Messages
2,570,574
Members
47,205
Latest member
ElwoodDurh

Latest Threads

Top