What a stupid gcc!

I

Ike Naar

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;

This is a syntax error; this z is undeclared.
The scope of the previous z ended at the closing brace of the block
where it was declared.

It is also a design error; if z is used in both branches of the
if statement, and also in the printf after the if statement,
it does not make sense to declare it in the first branch of the if statement,
it has to be declared at the level of the function body (where
the declarations of x and y are).
It only makes sense to declare z in the first branch of the if statement
if z is only used in that branch and nowhere else.
}

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;

This is not valid C; the grammar for an if statement is

if (expression) statement

or

if (expression) statement else statement;

"int z=3;" is a declaration, not a statement.

A statement can be a compound statement, i.e.

{ block-item-list }

where a block-item-list is a list of block-item,
and block-item is either a statement or a declaration.
So, in order to have "int z=3;" after the "if (x)", there
has to be a brace-enclosed block:

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.

The situation that you describe can not occur. The brace-enclosed block
has to be there in the first place.
 
B

BartC

Ike Naar said:
This is a syntax error; this z is undeclared.
The scope of the previous z ended at the closing brace of the block
where it was declared.

It is also a design error; if z is used in both branches of the
if statement, and also in the printf after the if statement,
it does not make sense to declare it in the first branch of the if
statement,
it has to be declared at the level of the function body (where
the declarations of x and y are).

So, this is a restriction on the ability to be able to declare variables
'anywhere'. Either it's an immediate error (a later use of the variable
brings it up), or the later variable mistakenly uses a more global version
(for example, a file-scope variable), or it works, but subsequent editing
removes/introduces/moves blocks around. Then you really need to keep an eye
on those declarations.

I thought it was supposed to make life easier?
It only makes sense to declare z in the first branch of the if statement
if z is only used in that branch and nowhere else.

As I'd understood it (up to now anyway), you could turn the first assignment
to a variable, into a declaration.
This is not valid C; the grammar for an if statement is

if (expression) statement

The following code compiles and works under Digital Mars' compiler:

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

with a subsequent error after 'int z=3' is enclosed in braces.
The situation that you describe can not occur. The brace-enclosed block
has to be there in the first place.

Maybe that's another DMC bug. I couldn't reproduce it with other compilers,
but that might have been them rejecting such declarations anyway.

In any case, it does highlight some issues when trying to declare variables
at first use; it's not always possible, or introduces restrictions on use.
 
B

Ben Bacarisse

BartC said:
OK, just have two loops then. You can't say a function is terrible because
it's got two separate nested loops.

No, it goes down from "terrible" to just plain "dodgy". Remember these
are triple-nested loops here.
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;

Eh? Is the fact that C permits something to be taken as a sign that C
considered it to be a benefit? C permits backwards gotos. C permits
single-character variable names. C permits multiple declarators in a
single declaration. These things are not always good and they are not
always bad. Saying "you only need one declaration rather six" does not
explain why one is better than six, unless there is some universal rule
that fewer is *always* better.

Personally, I don't see any advantage in simply reducing the number of
declarations at the expense of the size of their scope.
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.

So you are actually still saying that having one large-scope declaration
is better because it eases the introduction of gotos? I don't have time
to answer that, so if that is your view, I'll just leave it as a
"difference of opinion".
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.

No, still entirely baffled by what you obviously see as a problem. I am
not being facetious -- I honestly don't know what you are saying here.
Maybe you could illustrate the problem with an example or two?
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.

I don't get this. You say "the following code is wrong". Yes, it's
wrong. So what? I don't know what criticism of mixing statements and
declarations I'm suppose to take from these erroneous fragments.
 
L

Les Cargill

James said:
On 06/18/2012 09:46 PM, Paul J Gans wrote:
...

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


I can't say what ancient compiler chains did, but at
least since the '80s, the *linker* fixes up all the
tentative declarations ( unless I miss your meaning* ).

The compilation phase "knows" every jot and tittle except
externally referenced things.

*SFAIK, that's 100% "extern" stuff... in the case
of variably sized arrays, even that is fully nailed down
at compile time (by declarations of instances ) unless I've missed
something. It'll gen an error if you don't fully qualify
the size of something, and idioms like "extern long gee[]" do
not allow for knowing the size of "gee[]" in that scope.
 
J

James Kuyper

As I'd understood it (up to now anyway), you could turn the first assignment
to a variable, into a declaration.

You can, anywhere a declaration is permitted, but only if such a
declaration gives the variable's name sufficient scope to cover all
subsequent uses.

Declarations are permitted at file scope and as elements of blocks, but
there are a number of places in the grammar where a statement is
permitted, but a declaration is not: all declarations of x in following
lines are syntax errors:

label: int x=0;

if(i==3) int x=0;
else double x = 0;

while(a>3) int x = a--;

do int x = a++;
while(a < 100);

for(int i=0; i<100; i++) int x = i;

switch(a) int x=a;

switch(b)
{
case 25: int x = a;
break;
default: int x = b;
}

Every one of those cases can be dealt with by enclosing the declaration
in a block; in which case it would have a scope restricted to that
block. That would be pretty pointless unless that same block also
contained at least one statement that actually uses the variable.

....
The following code compiles and works under Digital Mars' compiler:

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

with a subsequent error after 'int z=3' is enclosed in braces.

Then it's not a conforming C compiler. There are only two C grammar
productions for an if statement, and that code doesn't fit either one:

if ( expression ) statement
if ( expression ) statement else statement

"int z=3;" is a declaration, not a statement. C++ has a concept of a
declaration-statement, which does not exist in C, so

if (x)
int z=3;

is permitted, but the C standard defines it as equivalent of

if (x)
{
int z=3;
}

so we can't explain DMC's behavior by assuming that it follows C++ rules.

Such a declaration would be pretty pointless in C, even if it were
allowed. However, in C++, if z had a type with a non-trivial constructor
or a non-trivial destructor, such a declaration could actually make sense.

....
Maybe that's another DMC bug. I couldn't reproduce it with other compilers,
but that might have been them rejecting such declarations anyway.

In any case, it does highlight some issues when trying to declare variables
at first use; it's not always possible, or introduces restrictions on use.

That's true, and well understood. Declaring variables at first use is
only possible when first use occurs at a location that would give the
variable sufficient scope to cover all subsequent uses. This is often
possible, but not always.
 
J

James Kuyper

James Kuyper wrote: ....

on => one
I can't say what ancient compiler chains did, but at
least since the '80s, the *linker* fixes up all the
tentative declarations ( unless I miss your meaning* ).

The compilation phase "knows" every jot and tittle except
externally referenced things.

*SFAIK, that's 100% "extern" stuff... in the case
of variably sized arrays, even that is fully nailed down
at compile time (by declarations of instances ) unless I've missed
something. It'll gen an error if you don't fully qualify
the size of something, and idioms like "extern long gee[]" do
not allow for knowing the size of "gee[]" in that scope.

Tentative definitions aren't 100% 'extern'. That keyword appears only
once in the code below, and that usage is unnecessary, because it
doesn't change anything. 'a' and 'b' below both have internal linkage.
However, if a tentative definition has internal linkage, it must have a
complete type, so such declarations don't cause problems for one-pass
compilation. However, I believe that the fact that the declarations for
'd' and 'e' below are allowed does make one-pass compilation a little
more difficult:

/* Tentative definitions: */
static int a, b;
int c;
int d[];
struct mystruct e;

/* Code which makes use of these variables, but never uses d or e in any
way that requires them to have a complete type. */

/* More tentative definitions: */
int d[10];
struct mystruct {
int i;
double d;
} e;

/* External definitions: */
static int a=1;
int b=2;

/* Neither a tentative definition nor an external definition */
extern int c;
 
B

BartC

Ben Bacarisse said:
I don't get this. You say "the following code is wrong". Yes, it's
wrong. So what?
I don't know what criticism of mixing statements and
declarations I'm suppose to take from these erroneous fragments.

I started my comments in this thread about the disadvantages of just
declaring variables anywhere as needed.

Then I found out about block scopes and all the extra hassle they introduce,
making such declarations even more problematic. (And why is a block so
special anyway? Surely it's only used when you need two or more statements
instead of one.)

I'm sorry if no-one else can see the problems (duplicate declarations,
clutter, separating related variables, scattered declarations, trickier
editing because you have to keep moving the declarations around, worrying
about scope, shadowing, anonymity, etc).

My point of view is also an implementer's one: with variables declared at
the top, the symbol table has a simple, tidy structure:

M Module
FN Function
A Variable
B Variable
C Variable

Module M, function FN, containing variables A,B,C; very easy. Now you
introduce block scopes, and multiple versions of 'A', 'B', 'C' all with
different types, scopes and lifetimes. The symbol table is no longer so
tidy, it now needs to be full of anonymous 'blocks'.

Given that the preference is this newsgroup is for small functions (usually
small enough to fit in a window), what's the point of adding this extra
complexity?
 
L

Lew Pitcher

On Tuesday 19 June 2012 16:13, in comp.lang.c, (e-mail address removed) wrote:
[snip]
I started my comments in this thread about the disadvantages of just
declaring variables anywhere as needed.

Then I found out about block scopes and all the extra hassle they
introduce, making such declarations even more problematic. (And why is a
block so special anyway? Surely it's only used when you need two or more
statements instead of one.)

That's one way of looking at it.

Another way to look at it would be that compound statements provide a level
of logical isolation that a single statement can't. They group together
multiple lines of code (declarations, statements) into a single entity, and
limit some of the side-effects of that entity.


[snip]
 
J

James Kuyper

On 06/19/2012 04:13 PM, BartC wrote:
....
I started my comments in this thread about the disadvantages of just
declaring variables anywhere as needed.

Then I found out about block scopes and all the extra hassle they introduce,
making such declarations even more problematic. (And why is a block so
special anyway? Surely it's only used when you need two or more statements
instead of one.)

What makes blocks special is their use as function bodies, and as the
code controlled by selection statements (if, switch) and iteration
statements (while, do-while, and for). There are not many good reasons
for creating a block other than those. Those reasons alone are
sufficient to give most code a fair number of distinct blocks.

The relevance to variable scope is that it is commonplace, when using
blocks for those purposes, to have variables which are needed only in a
particular block, and by declaring them inside such blocks and having
their scope restricted to those blocks, you reduce the likelihood of
interfering with the definitions of variables in other blocks.
I'm sorry if no-one else can see the problems (duplicate declarations,
clutter, separating related variables, scattered declarations, trickier
editing because you have to keep moving the declarations around, worrying
about scope, shadowing, anonymity, etc).

More accurately, I see most of the problems you've raised as positive
features. Duplicate definitions are good, precisely because they're
often not duplicates, but two different definitions of variables with
different meanings that should not be confused with each other, even if
you happened to make the mistake of giving them the same name.
Separating variables is good precisely because, if the appropriate scope
of those variables is in fact different, then they are usually
unrelated. If you tend to have a lot of related variables working
together with the same minimal scope, your program will often be
improved by putting those variables together in an aggregate: an array
or struct. Declarations are scattered to be close to their point of use,
where they're easier to find. Making editing difficult by requiring you
to move the declarations around is a good thing, because if you're doing
it a lot, something's wrong - you should give careful thought to each
such move. Having scope to worry about is a good thing - it means that
you can use a variable without worrying about the possibility that it
has already been used with some different meaning somewhere else in the
code. I've had to work on systems where all variables had global scope -
it's a nightmare.
 
J

Joe Pfeiffer

BartC said:
So, this is a restriction on the ability to be able to declare variables
'anywhere'. Either it's an immediate error (a later use of the variable
brings it up), or the later variable mistakenly uses a more global version
(for example, a file-scope variable), or it works, but subsequent editing
removes/introduces/moves blocks around. Then you really need to keep an eye
on those declarations.

I thought it was supposed to make life easier?

You can declare variables essentially anywhere, but where you declare
does have actual meaning.
As I'd understood it (up to now anyway), you could turn the first assignment
to a variable, into a declaration.

You can. But the declaration is only valid in the innermost block in
which it happens.
The following code compiles and works under Digital Mars' compiler:

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

with a subsequent error after 'int z=3' is enclosed in braces.

Why does this surprise you? In the code shown, the declaration is at
the level of the function -- the fact that it's in an if statement (but
not enclosed in a block) doesn't change that. When you create a block
to put it in, it's only valid inside that block.
Maybe that's another DMC bug. I couldn't reproduce it with other compilers,
but that might have been them rejecting such declarations anyway.

In any case, it does highlight some issues when trying to declare variables
at first use; it's not always possible, or introduces restrictions on use.

The restrictions are there whether you're declaring at first use or not.
 
B

Ben Bacarisse

BartC said:
I started my comments in this thread about the disadvantages of just
declaring variables anywhere as needed.

Then I found out about block scopes and all the extra hassle they
introduce, making such declarations even more problematic. (And why is
a block so special anyway? Surely it's only used when you need two or
more statements instead of one.)

I'm sorry if no-one else can see the problems (duplicate declarations,
clutter, separating related variables, scattered declarations,
trickier editing because you have to keep moving the declarations
around, worrying about scope, shadowing, anonymity, etc).

As I said when I commented, I sometimes get the feeling that what we
both do (that we call programming) is, in fact, not similar at all. I
don't split declarations unnecessarily and I don't separate related
ones. What you call "scattered", I call tidy. I don't think I spend
much time moving declarations around (but maybe I'm deluding myself on
that score) and I don't think I worry about scope.

Shadowing can occur using C90's rules and can even occur if you never
declare anything but at the top of a function. I just don't do it, and
I don't recall it being a problem. I don't know what the problem of
anonymity is.
My point of view is also an implementer's one: with variables declared
at the top, the symbol table has a simple, tidy structure:

M Module
FN Function
A Variable
B Variable
C Variable

Module M, function FN, containing variables A,B,C; very easy. Now you
introduce block scopes, and multiple versions of 'A', 'B', 'C' all
with different types, scopes and lifetimes. The symbol table is no
longer so tidy, it now needs to be full of anonymous 'blocks'.

It's stays simple if you allow the entries to be stacks: entering a new
scope just pushes the new data for that identifier. In fact, it can
simplify your model, because there is no need for the function level.
I've done it a few times, and I don't recall it being any trouble.
Given that the preference is this newsgroup is for small functions
(usually small enough to fit in a window), what's the point of adding
this extra complexity?

Extra complexity in the implementation is very low cost provided there
are lots of users. The more work the implementation has to do (i.e. the
most sophisticated the language) the less work the programmer has to do
(in general). Just allowing mixed declarations and code is neither much
work for the implementer, nor of much benefit to the programmer, so it's
not a particularly good example of the idea. But since it is not hard
to do, I'd say "why not?" rather than "why?".

[1] More accurately, it is criticism of what C sometimes gets used for.
C should stay simple, but more code should be written in higher-level
languages that are harder to implement.
 
G

Guest

SOS (if you're talking about the old DEC editor)

I was. My memory doesn't go far back enough to remmber what it could and couldn't do- but it was the most primitive editor I could think of.
Oh I've used batch command line editors. They were painful.
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.

the editor I normally use does syntax hi-lighting but otherwise I
wouldn't call it sophisticated. I use Visual Studio and taht can leap
straight to declations/definitions but taht's merely handy not essential.
It is
desirable for the source code of a language to be stored in a simple,
accessible format, not dependent on clever interpretive tools.

yes. Since I store source code in text files I'd say I met that
criterion. Declaring stuff near where it's used is *not* "dependent
on clever interpretive tools"
For example,
can you print off a copy of a function, direct from a file, and
read it?

well I'd have to select it. I don't do this very often unless the function
is badly written (I have one that takes up 13 pages). Normally I don't
need hard copy.
Having in-place and block-scope declarations, just crosses that line,
because it can make reading a paper (or dumb) copy more difficult.

Not significantly in my experience. Refactor your code so each function
fits on one page.
 
G

Guest

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.

err... I thought the claim was "only C has such restrictive
declaration rules". I thought C's rules were quite ordinary.
Perhaps I misunderstood what was being claimed. I was addressing
the claim that appears at the beginnign of this post.
 
J

James Kuyper

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.

err... I thought the claim was "only C has such restrictive
declaration rules". I thought C's rules were quite ordinary.
Perhaps I misunderstood what was being claimed. I was addressing
the claim that appears at the beginnign of this post.

Then you should have put your response immediately after that claim, to
reduce confusion. In the location where you actually put it, it gives
the false appearance of being intended as a response to a question about
"most (all?) subsequent languages".
 
B

BartC

Kenneth Brody said:
On 6/19/2012 4:13 PM, BartC wrote:
[...]
I started my comments in this thread about the disadvantages of just
declaring variables anywhere as needed.

Then I found out about block scopes and all the extra hassle they
introduce,
making such declarations even more problematic. (And why is a block so
special anyway? Surely it's only used when you need two or more
statements
instead of one.)

I'm sorry if no-one else can see the problems (duplicate declarations,
clutter, separating related variables, scattered declarations, trickier
editing because you have to keep moving the declarations around, worrying
about scope, shadowing, anonymity, etc).

Ahh... So you want all declarations to be global scope, always?

No. Function scope, file scope, and project-wide.
That eliminates the ability to do recursion (the "int i" within the
function is a global reference to the single "int i" variable within the
program).

It doesn't.
That eliminates the ability to create a "local" variable within a small
block of code (I can't have a small loop use "int x,y", as that would
simply be the same as the "double x,y" used elsewhere). That eliminates
basically everything useful about scope, because it eliminates scope
entirely.

No-one has said anything of the kind. Scope would still exist across
modules,
across one module, and across one function. I'm saying it doesn't need to be
across individual blocks too, given that a typical function is small.
Given that the preference is this newsgroup is for small functions
(usually

How could one write a "small function" if it couldn't create variables
that are available only within its scope?

We're talking about adding extra block scopes to a 10- or 20-line function
that already has local variables valid throughout the function.

How small a scope do you need: per line, per statement, or per expression?
 
B

BartC

Kenneth Brody said:
Kenneth Brody said:
On 6/19/2012 4:13 PM, BartC wrote: [...]
I'm sorry if no-one else can see the problems (duplicate declarations,
clutter, separating related variables, scattered declarations, trickier
editing because you have to keep moving the declarations around,
worrying
about scope, shadowing, anonymity, etc).

Ahh... So you want all declarations to be global scope, always?

No. Function scope, file scope, and project-wide.

So this:

void foo(void)
{
int i;
...
}

should (in your opinion) create a variable "i" of type integer with a
scope of the braces that encloses the declaration (ie: the function
"foo"), but:

void foo(void)
{
...
if ( bar )
{
int i;
...
}
...
}

should behave differently, and give "i" some scope other than the braces
that enclose the declaration?

Until a few days ago, I'd never even considered that variables could be
declared local to a block. You second example was, to me, just a convenient
way of declaring 'i' near the place where you started using it, but would
have exactly the same scope as the ones at function level (ie. declared at
the top of the function).

Now it seems the 'i' is limited to it's immediately enclosing braces, which
doesn't appear so useful. Suppose it's an if-else statement, both branches
having code that uses a variable 'i' of the same type; why bother declaring
two distinct versions of it?
[...]
No-one has said anything of the kind. Scope would still exist across
modules,
across one module, and across one function. I'm saying it doesn't need to
be
across individual blocks too, given that a typical function is small.

if ( foo.type == MyInteger )
{
int num;
num = foo.integer;
... do stuff with int ...
}
else if ( foo.type == MyFloat )
{
float num;
num = foo.floating;
... do stuff with float ...
}

I accept that it does allow you to do stuff like this, but it really doesn't
seem a very good idea, using the same identifier for two different types,
within a few lines of each other in the same function.

Imagine you're trying to read such a function, and your eyes jump from one
part of the function to another. First num is a int; fine. Now you jump to
the 'do stuff with float' part, and you have to do a double take. From this
point on, you can't trust anything you see; what type is 'num' going to have
next time you see it? Maybe these are two nested blocks in a loop, and you
break from one to the other. You have to trace the code, character by
character counting blocks, to match the use of 'num' with it's correct
declaration.

Sorry, but these possibilities which everyone else thinks are great, to me
just seem nightmarish.
[...]
We're talking about adding extra block scopes to a 10- or 20-line
function
that already has local variables valid throughout the function.

How small a scope do you need: per line, per statement, or per
expression?

Why should the scope of a variable declared inside the outermost braces of
a function be different from a variable within some other set of braces?
What's difficult about the concept that the scope is the innermost set of
braces that enclose the declaration?

What is a set of braces? C uses braces to:

- Enclose the statements of a function body (which can't have just one
non-block statement).

- Enclose an initialiser expression or list

- Group several ordinary statements into one. C statements such as while,
for and if only allow one statement in a loop body or each branch of the
'if', so {, } are a mechanism for allowing more than one.

The syntax could have used entirely different symbols for these
requirements. Having a local scope for a function body, that's fine; every
language except Basic has that. But what's so special about a compound
statement that it ought to have it's own private set of variables?

On just about every other occasion where the complexity of a function grows
beyond about 3 lines, I'm constantly told it needs to split into
sub-functions. Now entire, self-contained sub-programs can be created in
each branch of a minor 'if' in a minor 'for' loop in a dusty corner of a
function, and that is perfectly OK?

In a different kind of forum from comp.lang.c, I would almost start to
suspect a wind-up!
 
B

Ben Bacarisse

BartC said:
Having a local scope for a function body, that's fine;
every language except Basic has that. But what's so special about a
compound statement that it ought to have it's own private set of
variables?

I hope you won't think me rude (and please tell me to mind my own
business if you think the question impertinent) but what programming
languages have had experience of? Block structure like this has been
around since... actually I don't know but Algol had it in the late 50s.

I had to think hard to come up with a structured language without it,
but Pascal is a rare example. It has nested function declarations, but
plain blocks can't nest declarations.

Your question "what's so special about a compound statement" is, to my
mind, the wrong way round. What's so special about the block that forms
a function body that it alone is allowed to contain declarations?
On just about every other occasion where the complexity of a function
grows beyond about 3 lines, I'm constantly told it needs to split into
sub-functions. Now entire, self-contained sub-programs can be created
in each branch of a minor 'if' in a minor 'for' loop in a dusty corner
of a function, and that is perfectly OK?

My liking for small helper functions is in fact thwarted by the fact
that C's syntax takes a leaf out of your book. While variable
declarations can be nested in enclosed blocks, function definitions
can't be. Perhaps it would be done more often if one could do this:

int some_function(int l, int m, int n, int mat[l][m][n])
{
int none_zero()
{
for (int i = 0; i < l; i++)
for (int j = 0; j < m; j++)
for (int k = 0; k < n; k++)
if (mat[j][k] == 0)
return 0;
return 1;
}

if (none_zero()) /* ... */
/* do stuff */
if (/*still */ none_zero()) /* ... */
/* ... */
}

(yes, I know gcc allows such things when not is standard C mode.)
In a different kind of forum from comp.lang.c, I would almost start to
suspect a wind-up!

No, I think you've just had a very different exposure to programming
languages and that's affected what you think is usual, helpful,
troublesome and so on.
 
G

Guest

*what* extra hassle?

why not? I think you've just discovered that auto variables are attached
to *blocks* not just *function bodies*. Amazes me how people can program
for years in the language and not know this! I guess they code by copy-paste
rather than reading a manual or the language definition (C's official
definition is quite readable).

On an historical note. C is based on Algol-60 and Algol-60 allowed nested
functions so nesting blocks with declarations probably didn't look so odd
as you were already nesting functions with declarations. There are problems
with nested functions and "global" variables.

int f ()
{
int i = 1;

int g ()
{
i = i + 1;
}

int h ()
{
int i = 2;
g ();
}

print (i);
}

so which i is g() updating? Algol-60 answered "the one in h()" (dynamic scope)
whilst Pascal answered "the one in f()" (lexical scope). C avoided the problem
by disallowing nested functions- and you only occaisionally miss them.

or wish to declare a local variable!

you can have those anyway with file and block scope and also function parameters.

I see no clutter

so don't do that

honest it isn't a problem!

you're doing something wrong if you do this a lot.
worrying about scope, shadowing,

I don't worry about these
anonymity, etc).

and I don't even know what *that* is!
As I said when I commented, I sometimes get the feeling that what we
both do (that we call programming) is, in fact, not similar at all. I
don't split declarations unnecessarily and I don't separate related
ones. What you call "scattered", I call tidy. I don't think I spend
much time moving declarations around (but maybe I'm deluding myself on
that score) and I don't think I worry about scope.

100% agree
Shadowing can occur using C90's rules and can even occur if you never
declare anything but at the top of a function. I just don't do it, and
I don't recall it being a problem.

I have done it and again I didn't notice a problem.
I don't know what the problem of anonymity is.

I've only limited interest in the implementor's problems

I don't think it was ever as tidy as you thought it was

int x;
int y;

int f (int x)
{
int y;
}

int main (void)
{
int x;

f (x);
f (y)
}

I'll note Algol-60 was invented in 1960 and *they* managed to implement it.
Admittedly "they" were really smart European computer scientists and
mathematicians.
It's stays simple if you allow the entries to be stacks: entering a new
scope just pushes the new data for that identifier. In fact, it can
simplify your model, because there is no need for the function level.
exactly

I've done it a few times, and I don't recall it being any trouble.

it isn't really any increase in complexity. And again note
*** this is not a new feature*** C always worked this way!
But yes with small functions the need for it lessens.
Extra complexity in the implementation is very low cost provided there
are lots of users. The more work the implementation has to do (i.e. the
most sophisticated the language) the less work the programmer has to do
(in general). Just allowing mixed declarations and code is neither much
work for the implementer, nor of much benefit to the programmer,

C++ programmers would disagree
so it's
not a particularly good example of the idea. But since it is not hard
to do, I'd say "why not?" rather than "why?".

[1] More accurately, it is criticism of what C sometimes gets used for.
C should stay simple, but more code should be written in higher-level
languages that are harder to implement.

the reverse logic is to do everything in Dartmouth BASIC. Note sophisticated
languages aren't always harder to implement. FORTRAN IV was notoriously difficult
to implement and program in because of its poor orthogonality.

An example where block scope makes sense in C++ :-

void func ()
{
{
Lock lock (table_semaphore);
do_stuff_on_locked_table();
do_more_stuff_on_locked_table();
}

long_operation_without_lock()
}

Lock's destructor cleans up the semaphore
 
G

Guest

Kenneth Brody said:
On 6/19/2012 4:13 PM, BartC wrote:
[...]
I started my comments in this thread about the disadvantages of just
declaring variables anywhere as needed.

Then I found out about block scopes and all the extra hassle they
introduce,
making such declarations even more problematic. (And why is a block so
special anyway? Surely it's only used when you need two or more
statements
instead of one.)

I'm sorry if no-one else can see the problems (duplicate declarations,
clutter, separating related variables, scattered declarations, trickier
editing because you have to keep moving the declarations around, worrying
about scope, shadowing, anonymity, etc).

Ahh... So you want all declarations to be global scope, always?

No. Function scope, file scope, and project-wide.
That eliminates the ability to do recursion (the "int i" within the
function is a global reference to the single "int i" variable within the
program).

It doesn't.
That eliminates the ability to create a "local" variable within a small
block of code (I can't have a small loop use "int x,y", as that would
simply be the same as the "double x,y" used elsewhere). That eliminates
basically everything useful about scope, because it eliminates scope
entirely.

No-one has said anything of the kind. Scope would still exist across
modules,
across one module, and across one function. I'm saying it doesn't need to be
across individual blocks too, given that a typical function is small.
Given that the preference is this newsgroup is for small functions
(usually

How could one write a "small function" if it couldn't create variables
that are available only within its scope?

We're talking about adding extra block scopes to a 10- or 20-line function
that already has local variables valid throughout the function.

How small a scope do you need: per line, per statement, or per expression?

for (int i = 0; i < 10; i++) sum += i;

I believe C++ now allows declarations in whils and ifs (?)
 
G

Guest


<snip>

[why block scope?]
I accept that it does allow you to do stuff like this, but it really doesn't
seem a very good idea, using the same identifier for two different types,
within a few lines of each other in the same function.

Imagine you're trying to read such a function, and your eyes jump from one
part of the function to another. First num is a int; fine. Now you jump to
the 'do stuff with float' part, and you have to do a double take. From this
point on, you can't trust anything you see; what type is 'num' going to have
next time you see it? Maybe these are two nested blocks in a loop, and you
break from one to the other. You have to trace the code, character by
character counting blocks, to match the use of 'num' with it's correct
declaration.

Sorry, but these possibilities which everyone else thinks are great, to me
just seem nightmarish.

don't try programming in a dynamically typed language then!

func fred ()
{
var = 1.02; // var is a float
some_code ()
var = "hello"; // now it's a string
}

[this isn't any particular language- Python or scheme can do this
sort of stuff but with different syntax]

I'm also not saying it's necessarily a good idea to do it willy-nilly.

On just about every other occasion where the complexity of a function grows
beyond about 3 lines, I'm constantly told it needs to split into
sub-functions.

I wish someone had told the guy who wrote the software I'm maintaining that...
Now entire, self-contained sub-programs can be created in
each branch of a minor 'if' in a minor 'for' loop in a dusty corner of a
function, and that is perfectly OK?

In a different kind of forum from comp.lang.c, I would almost start to
suspect a wind-up!

take a look a some idiomatic C++. One argument is you don't declare anything
until you know how to initialise it.

There are an awful lot of languages that allow block scope variables!
 

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,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top