My experience does not match your assertion.
Clearly not. I commonly have to read code, and I find it takes
several
times as long to read C++ code precisely because its so hard to track
down where a variable has been declared. Ordinarily, I would expect
that
if its not declared at the top of some enclosing scope then it is
either
a global or an attribute of "this" object. Except its not -- because
I
have to check every for(...) and in fact, every line of code (except
disjoint scopes) up until the start of the function.
The main advantage of C89 is that it really is just the top of each
enclosing scope and it can only be a local, a file scope static or an
extern global. Its typically extremely fast to scan for the first
two,
leaving the last check (looking through .h files) unnecessary. But
usually the difference between a file static/global and an extern
global
is not relevant to any given function -- you just want to know if a
variable is local. In C89, you know that with a quick scan by eye
with
a page-up keystroke or so.
With C99 we are now forced to scan every line of code up to the start
of each function.
Some will chime in and claim that you should be using some search
function in your editor or some source code browser. But that's
nonsense
as you typically can only do *one* of those at a time. I.e., you
might
be doing a "find next" on some important thing you are searching for
that
you want to retain the state for, and while you are doing so you want
to
check where a potential local is being declared without clearing your
search buffer. Furthermore, some editors (such as the Visual C++ text
editor) don't have a backwards search. This is not some fanciful
scenario
I am artificially constructing -- I *DO* this nearly every day.
Only if you know which scope it is defined in.
And just how deeply scoped do you write your code? There is an
explicit
syntactical pointer; namely the "{" character. Most people also
indent
their code to make finding these "{" characters visually obvious.
[...] Since scopes nest in C,
there's no way to be certain unless declarations are restricted not
merely to the top of the scope, but to the top of the function.
This is the very definition of pedantry. The problem of finding the
top
of a function is not significantly different from finding the set of
scope
beginnings up to the top of the function declaration. The depth of
your
scopes is typically O(log(lines in function)).
[...] I would
argue against the "top of the function" solution, but since you don't
seem to be advocating it, I won't bother.
Since a large fraction of the code I've ever worked on was written by
other people, there's no guarantee that any given variable is declared
in any particular scope, no matter what I might prefer. I have to do
the same kind of backwards search to find the top of the right scope
that I would have to perform to find a declaration that's not at the top
of a scope, so I don't see how it saves me any trouble.
Search backwards through indents or "{"s is the same as scanning line
by
line to you?
They also make the program harder to read and comprehend, in my experience.
Odd. I find myself swinging in the other direction. Very often a
function
does things in distinct steps, where is it a valuable hint to separate
these
by scopes -- that typically corresponds exactly to where you want
separate
scopes to divide your local variables.
In my experience, the search is easier whether I use my eyes or other
tools to perform it.
In my experience, code is clearer when definitions are closer to first
use, than it is when they're artificially constrained to be declared
somewhere prior to first use.
Well we clearly just have radically different experience.
The main problem with your approach is that the closest first use
might
be *wrong* since the local variable can be used early *and* late in a
function, in which case its got to be put into a high enough position
in an enclosing scope anyways:
type function (...) {
/* A */ ...
if () {
... /* Don't touch x */
}
... /* B */ ...
switch () {
... /* Don't touch x */
}
... /* C */ ...
for (/* D */ ...) {
/* E */
...
touch (&x); /* <-- Looking here */
...
}
...
return x; /* <-- Or looking here */
}
So if you happen to be scanning either either of the places which are
marked "looking here" you need to check A, B, C, D (and possibly E)
for
the declaration of x. In C89 you only need to check A (and possibly
E).
In fact you also need to check more lines at A (and possibly E) with
C99/C++.
This isn't just some worst case -- if x is a static or global (or
object attribute) you will commonly see code like this and have to
scan
all those positions before you realize it is such.