why do some writers declare and define variables separately

Y

yoodavid

Can anyone tell me why some writers decide to declare and
write definitions for variables separately, rather than
together?

That is:
int definelater;
....
definelater = 0;

rather than:
int definelater = 0;

wouldn't the later reduce code lines?
thanks.
 
F

Fred K

Can anyone tell me why some writers decide to declare and write definitions for variables separately, rather than together? That is: int definelater; ... definelater = 0; rather than: int definelater = 0; wouldn't the later reduce code lines? thanks. -- to authenticate is not to authorize. be responsible. -- comp.lang.c.moderated - moderation address: (e-mail address removed) -- you must have an appropriate newsgroups line in your header for your mail to be seen, or the newsgroup name in square brackets in the subject line. Sorry.

This is really a style issue. Each person has different preferences; some come form historic use with other languages.

You may not know what value to initialize it to. For example:
int xxx;
if ( something ) {
xxx = readValueFormFile(...);
else if ( somethingElse ) {
xxx = getValueFromDatabase(...);
else {
xxx = popDialogAndGetValue(...);
else {
return errorValue;
}
 
E

Eric Sosman

Can anyone tell me why some writers decide to declare and
write definitions for variables separately, rather than
together?

That is:
int definelater;
...
definelater = 0;

rather than:
int definelater = 0;

wouldn't the later reduce code lines?

Yes, and it has some other potential advantages as well.
However, "Old Time Original C As Our Forefathers Knew It" had
a restriction: All the declarations in a {}-enclosed block
had to precede all the executable statements. This restriction
was removed by the 1999 edition of the C Standard, but (even
to this day!) some compilers have not yet caught up to 1999.
If you're writing code that might need to work with those
outdated compilers -- or if your habits were formed before
the restriction was lifted -- you'll group the declarations
at the start of each block.
 
K

Keith Thompson

yoodavid said:
Can anyone tell me why some writers decide to declare and
write definitions for variables separately, rather than
together?

That is:
int definelater;
...
definelater = 0;

rather than:
int definelater = 0;

wouldn't the later reduce code lines?

Your terminology is a bit off. This line:

int definelater;

both *declares* and *defines* the variable. (Roughly, declaring a
variable makes its name visible, and defining it allocate storage for
it.) The "= 0", when it's part of the declaration is an *initializer*.
When written separately, "definelater = 0;" is an *assignment*.

Usually it's better, when possible, to use an initializer rather than a
separate assignment -- not because it saves lines of code, but because
it's clearer.

One case where a separate assignment is more appropriate is when you
don't know, at the point of the declaration, what value should be
assigned:

int obj;
if (some_condition) {
obj = this_value;
}
else {
obj = that_value;
}

The ability to mix declarations and statements, introduced in C99, can
alleviate this in some cases, making it easier to move the declaration
to the point where you know how to initialize it:

int x = some_value;
int y;
some_func(&y);
int z = x + y;
 
J

Jens Schmidt

yoodavid said:
Can anyone tell me why some writers decide to declare and
write definitions for variables separately, rather than
together?

Your code doesn't have any separate declaration. What is separate are
definition and initialisation. A declaration would be e.g. a line
extern int definelater;
at the very begining.

As I can't read the mind of "some writers", here are some guesses:
Early versions of C didn't have the possibility to define variables
everywhere in a function. You had to first define or declare all names
used in the function and only then start with statements.
Complex types, i.e. struct and union types, are not always initializeable
inside the definition. So you have to initialize in separate statements.
Now the programmer initialiazes everything this way for consistency.
I'd categorize that as marginally valid at its time, but obsolete with
modern usage.
wouldn't the later reduce code lines?

Reduction of code lines a non-relevant argument nowadays. The most expensive
resource in computing is the programmer. So the criteria for coding style
are a) is it clearly visible from the source code, what the intention was
and b) is it improbable to introduce errors, especially later when changing
the code.
Actually the result is the same: initialize as soon after definition.
 
B

Ben Bacarisse

Francis Glassborow said:
That is a definition as well as a declaration. However the variable
has not been initialised

A couple of minor points... If that line is at file scope, it's a
"tentative definition" and, if nothing happens to change things, it will
behave as if there were an initialiser (of zero).
Well that is just an assignment.

Technically, no. It's covered by the rules for initialising rather than
for assigning.

<snip>
 
E

Eric Sosman

[...]
One thing to remember is that in
classic C all variables had to be declared/defined before any other
executable statements in a block of code. Sometimes it was simply not
possible to know what the value would be so initialising it at the point
of definition might be considered to be a waste of time.

Having said that, it is almost always bad code to leave a variable in an
uninitialised state. It is a recipe for future problems.

In modern "declare it whenever you like" C, I'd agree:
It is almost always better to postpone defining a variable
until you've got a reasonable initialization value. However,
if one must define earlier I feel it is usually *not* a good
idea to fabricate an initializer out of thin air. The common
advice "Initialize all pointer variables to NULL" is, I think,
a particularly bad example of bad practice.

That's not to say it was always a bad practice. A quarter
century ago compilers ran on sluggish systems in cramped memory
spaces; the basic job of translating the source code took all
the resources at their disposal and there was little left over
for dataflow analysis. If you wrote

char *evenOrOdd(int x) {
char *p;
switch (x % 2) {
case 0: p = "even"; break;
case 1: p = "odd"; break;
}
return p;
}

.... few old-time compilers would warn you that `p' might be
uninitialized in the `return' statement (because `x % 2' could
be -1). In those days, writing

char *evenOrOdd(int x) {
char *p = NULL;
switch (x % 2) {
case 0: p = "even"; break;
case 1: p = "odd"; break;
}
return p;
}

.... could be justified because it would likely give a reproducible
failure, easier to track down and solve than a Heisenbug.

But that was a quarter century ago, when machines with a few
tens of megabytes ran at a few tens of megahertz. That was back
in the days when the `register' keyword sometimes made sense. Do
you still use `register', or do you rely on the compiler's more
thorough code analysis? If the latter, why try to defeat that
same analysis when it comes to possibly uninitialized variables?

Here's what I mean by the "defeat" remark: With today's
compilers the first version of evenOrOdd() will almost certainly
elicit a diagnostic, and your attention will be drawn immediately
to the faulty code. But the second will almost certainly *not*
draw a complaint, because the compiler can see that the variable
*is* initialized -- to a garbage value, but the compiler doesn't
know NULL is garbage. Instead of detecting the bug at compile
time you detect it in testing (if you're lucky) or in deployment
(if less than lucky) or on final approach to Mars. The safeguard
that long ago improved the bug from "awful" to merely "bad" now
prevents further improvement to "averted." It's a stratagem that
refuses the compiler's offer of help -- and if you're like me,
you shouldn't refuse help.

Don't just initialize variables for initialization's sake.
It's a superstition both outmoded and harmful.
 
L

Les Cargill

Eric said:
[...]
One thing to remember is that in
classic C all variables had to be declared/defined before any other
executable statements in a block of code. Sometimes it was simply not
possible to know what the value would be so initialising it at the point
of definition might be considered to be a waste of time.

Having said that, it is almost always bad code to leave a variable in an
uninitialised state. It is a recipe for future problems.

In modern "declare it whenever you like" C, I'd agree:
It is almost always better to postpone defining a variable
until you've got a reasonable initialization value. However,
if one must define earlier I feel it is usually *not* a good
idea to fabricate an initializer out of thin air. The common
advice "Initialize all pointer variables to NULL" is, I think,
a particularly bad example of bad practice.

IMO, it goes back to many architectures trapping on dereferencing a NULL
pointer. It was a way of adding "terminating resistors" to code;
obviating Heisenbugs.
That's not to say it was always a bad practice. A quarter
century ago compilers ran on sluggish systems in cramped memory
spaces; the basic job of translating the source code took all
the resources at their disposal and there was little left over
for dataflow analysis. If you wrote

char *evenOrOdd(int x) {
char *p;
switch (x % 2) {
case 0: p = "even"; break;
case 1: p = "odd"; break;
}
return p;
}

... few old-time compilers would warn you that `p' might be
uninitialized in the `return' statement (because `x % 2' could
be -1). In those days, writing

char *evenOrOdd(int x) {
char *p = NULL;
switch (x % 2) {
case 0: p = "even"; break;
case 1: p = "odd"; break;
}
return p;
}

... could be justified because it would likely give a reproducible
failure, easier to track down and solve than a Heisenbug.

I would use this:

char *p = ( ( x % 2 ) == 0 ) ? "even" : "odd" ;

or

const char * const mInit[] = { "even", "odd" } ;

char *p = (char *)mInit[(x % 2)];

The point is to have a ... violently enforced
constraint on p that is completely invariant, no matter what.

That's more likely to be acceptable no matter the toolchain, unless
you're in a really old compiler.

If you need logic more complex than that, write an initializer
routine.
But that was a quarter century ago, when machines with a few
tens of megabytes ran at a few tens of megahertz. That was back
in the days when the `register' keyword sometimes made sense. Do
you still use `register', or do you rely on the compiler's more
thorough code analysis? If the latter, why try to defeat that
same analysis when it comes to possibly uninitialized variables?

Here's what I mean by the "defeat" remark: With today's
compilers the first version of evenOrOdd() will almost certainly
elicit a diagnostic, and your attention will be drawn immediately
to the faulty code. But the second will almost certainly *not*
draw a complaint, because the compiler can see that the variable
*is* initialized -- to a garbage value, but the compiler doesn't
know NULL is garbage. Instead of detecting the bug at compile
time you detect it in testing (if you're lucky) or in deployment
(if less than lucky) or on final approach to Mars. The safeguard
that long ago improved the bug from "awful" to merely "bad" now
prevents further improvement to "averted." It's a stratagem that
refuses the compiler's offer of help -- and if you're like me,
you shouldn't refuse help.

Don't just initialize variables for initialization's sake.
It's a superstition both outmoded and harmful.


I rather doubt that. One of the more useful things from OO is
RAII - Resource Allocation Is Initialization. There are at least
analogs in non-OO; this is one.

You may well be saying the same thing and I missed it; your point is
unclear to me.
 
N

Noob

Les said:
Eric said:
[...] `p' might be uninitialized (because `x % 2' could be -1).
^^^^^^^^^^^^^^^^^^^^^^^^^^^

I would use this:
[...]
const char * const mInit[] = { "even", "odd" } ;
char *p = (char *)mInit[(x % 2)];

And panic ensues when x % 2 equals -1?
 
K

Ken Brody

Can anyone tell me why some writers decide to declare and
write definitions for variables separately, rather than
together?

That is:
int definelater;
...
definelater = 0;

rather than:
int definelater = 0;
[...]
In any case, compilers nowadays are pretty smart, and I'm pretty sure that
with the right options (-O3), they would optimize the separate calls into a
single define and initialize.

I'm not sure what you are saying about optimizing, since there is nothing to
"optimize" here. The statement "int definelater;" generates no code(*).

(*) Okay, if "definelater" is the *only* variable in the function, then it
make take some code to reserve space for the local variable. However, that
same code would be required for "int definelater=0;".
 
K

Keith Thompson

Les Cargill said:
I would use this:

char *p = ( ( x % 2 ) == 0 ) ? "even" : "odd" ;

or

const char * const mInit[] = { "even", "odd" } ;

char *p = (char *)mInit[(x % 2)];

The point is to have a ... violently enforced
constraint on p that is completely invariant, no matter what.

That last one can still have undefined behavior when x % 2 == -1.
Following the pattern of your first example, you could write it as:

char *p = (char *)mInit[(x % 2) == 0];

And since p is expected to point to a string literal, it should be const
(the same criticism applies to Eric's code upthread), which neatly
avoids any need for casts. I might write your two examples as:

const char *p = x % 2 == 0 ? "even" : "odd";

and

const char *const mInit[] = { "even", "odd" };
const char *p = mInit[x % 2 == 0];

(probably the former, since I find it clearer).

[...]
 
E

Eric Sosman

Les Cargill said:
I would use this:

char *p = ( ( x % 2 ) == 0 ) ? "even" : "odd" ;

or

const char * const mInit[] = { "even", "odd" } ;

char *p = (char *)mInit[(x % 2)];

The point is to have a ... violently enforced
constraint on p that is completely invariant, no matter what.

That last one can still have undefined behavior when x % 2 == -1.
Following the pattern of your first example, you could write it as:

char *p = (char *)mInit[(x % 2) == 0];

... producing "odd" when x is even ...
 
K

Keith Thompson

Eric Sosman said:
Les Cargill said:
I would use this:

char *p = ( ( x % 2 ) == 0 ) ? "even" : "odd" ;

or

const char * const mInit[] = { "even", "odd" } ;

char *p = (char *)mInit[(x % 2)];

The point is to have a ... violently enforced
constraint on p that is completely invariant, no matter what.

That last one can still have undefined behavior when x % 2 == -1.
Following the pattern of your first example, you could write it as:

char *p = (char *)mInit[(x % 2) == 0];

... producing "odd" when x is even ...

Another argument for using the one-line version, since it makes the
meaning clearer; the name "mInit" doesn't add any useful information
to the reader.

(I'm not trying either to suggest that terse code is automatically
better or to excuse my own silly error, but it does (accidentally)
illustrate a point.)
 
E

Eric Sosman

Eric Sosman said:
[...]
I would use this:

char *p = ( ( x % 2 ) == 0 ) ? "even" : "odd" ;

or

const char * const mInit[] = { "even", "odd" } ;

char *p = (char *)mInit[(x % 2)];

The point is to have a ... violently enforced
constraint on p that is completely invariant, no matter what.

That last one can still have undefined behavior when x % 2 == -1.
Following the pattern of your first example, you could write it as:

char *p = (char *)mInit[(x % 2) == 0];

... producing "odd" when x is even ...

Another argument for using the one-line version, since it makes the
meaning clearer; the name "mInit" doesn't add any useful information
to the reader.

(I'm not trying either to suggest that terse code is automatically
better or to excuse my own silly error, but it does (accidentally)
illustrate a point.)

My original point was about wanton initialization doing harm
by suppressing a helpful diagnostic, and I wrote the function as
a brief illustration of how suppression could occur. "Brief" and
"buggy" were the goals; the function's, er, function was beside
the, er, point.

It is interesting that two count them two attempts to improve
my silly, trivial, and intentionally buggy function have added new
bugs in the name of clarity. This demonstrates a new point -- I'm
not sure what the point is, exactly, but it's not a sharp one.
 
T

Tim Rentsch

Les Cargill said:
[snip]

One of the more useful things from OO is RAII - Resource
Allocation Is Initialization.

RAII is a C++ -ism. It has nothing to do with OOP.
 
T

Tim Rentsch

Francis Glassborow said:
Can anyone tell me why some writers decide to declare and
write definitions for variables separately, rather than
together?
[snip]

Consider:

int foo (int test){
int i;
if (test > 0) i = test;
else i = -test;
/+ rest of code */

}

[snip] Sometimes it was simply not possible to know what the
value would be so initialising it at the point of definition
might be considered to be a waste of time.

I agree with the principle but this example isn't a good
one to illustrate it:

int foo (int test){
int i = test > 0 ? test : -test;
...
}
 
T

Tim Rentsch

Eric Sosman said:
Eric Sosman said:
On 9/19/2013 11:31 AM, Keith Thompson wrote:
[...]
I would use this:

char *p = ( ( x % 2 ) == 0 ) ? "even" : "odd" ;

or

const char * const mInit[] = { "even", "odd" } ;

char *p = (char *)mInit[(x % 2)];

The point is to have a ... violently enforced
constraint on p that is completely invariant, no matter what.

That last one can still have undefined behavior when x % 2 == -1.
Following the pattern of your first example, you could write it as:

char *p = (char *)mInit[(x % 2) == 0];

... producing "odd" when x is even ...

Another argument for using the one-line version, since it makes the
meaning clearer; the name "mInit" doesn't add any useful information
to the reader.

(I'm not trying either to suggest that terse code is automatically
better or to excuse my own silly error, but it does (accidentally)
illustrate a point.)

My original point was about wanton initialization doing harm
by suppressing a helpful diagnostic, and I wrote the function as
a brief illustration of how suppression could occur. "Brief" and
"buggy" were the goals; the function's, er, function was beside
the, er, point.

The problem is the example you gave doesn't make your point
convincingly. To some degree it even argues against it - the
question of whether or not a variable should be initialized may
reasonably raise a red flag about the approach taken. And
justifiably so I would say, since the asked-for functionality
can be implemented without any variables at all:

return x%2 ? "odd" : "even";
It is interesting that two count them two attempts to improve
my silly, trivial, and intentionally buggy function have added new
bugs in the name of clarity. This demonstrates a new point -- I'm
not sure what the point is, exactly, but it's not a sharp one.

My primary takeaway is if one to make a convincing argument about
programming practices, the examples used should be taken from
real-life code rather than just made up for the purpose of the
argument.
 
Ö

Öö Tiib

RAII is a C++ -ism. It has nothing to do with OOP.

Something it has to do. Destructors have something to do with
OOP (being in set of OOP features of some languages). RAII has
something to do with destructors since it is idiomatic usage of
destructors for binding life-time of resources to scope of variables
that encapsulate the resources.

It may be C++ -ism that has something to do with OOP, since
Stroustrup invented it. However it feels like useful idiom for
Ada and for D as well. Someone using Ada or D actively could say
better.
 
T

Tim Rentsch

Tiib said:
RAII is a C++ -ism. It has nothing to do with OOP.

Something it has to do. Destructors have something to do with
OOP (being in set of OOP features of some languages). [snip
elaboration.]

Object-oriented programming - both the term and the concept -
predates not only C++-style destructors but C++ itself.
Anyone who thinks destructors are part of OOP doesn't
understand the term.
 

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,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top