Initialising Variables

K

Keith Thompson

Richard Heathfield said:
Keith Thompson said:

I understand what you're saying, but I'm struggling to understand why you're
saying it. Yes, incorrect results can be arbitrarily subtle and difficult
to detect. Does that mean we're allowed to produce incorrect results, or
not?

By definition, incorrect results are ones that we're not allowed to
produce.
If we're /not/ allowed to produce incorrect results, then our testing
had better be able to spot arbitrarily subtle output errors.
[...]

Yes, it had better -- but in real life, testing cannot always catch
100% of all errors. If perfect testing were possible, we could
probably apply the same techniques to write perfect code and eliminate
the need for testing in the first place. And a number of space probes
would have been a lot more successful.

Obviously we should try to avoid errors. But it can be useful to
arrange for any errors that slip through to manifest themselves in as
unsubtle a matter as possible.

Sometimes. YMMV. Yadda yadda.
 
Y

Yevgen Muntyan

Keith said:
gcc 3.4.4 produces a warning with "-O1" or higher. gcc 4.1.1 produces
a warning with "-O3" or higher.

Thanks for catching this. With -O3 it inlines func(), and it does
warn if you use 'if (0) foo = 8'. Code below doesn't:

#include <stdio.h>

int func (void);

int main (void)
{
int foo;

if (func ())
foo = 8;

printf ("%d\n", foo);
return 0;
}

(or replace func() with rand(), etc.)

Thanks,
Yevgen
 
R

Richard Heathfield

Keith Thompson said:
If we're /not/ allowed to produce incorrect results, then our
testing
had better be able to spot arbitrarily subtle output errors.
[...]

Yes, it had better -- but in real life, testing cannot always catch
100% of all errors. If perfect testing were possible, we could
probably apply the same techniques to write perfect code and eliminate
the need for testing in the first place. And a number of space probes
would have been a lot more successful.

Obviously we should try to avoid errors. But it can be useful to
arrange for any errors that slip through to manifest themselves in as
unsubtle a matter as possible.

Very true, but undefined behaviour can be very subtle.

Here's a strategy that would combine your intent with mine, producing a
result that I suspect neither of us would like very much:

int idx = INT_MIN;

/* arbitrary code fragment elided */

if(idx < 0)
{
error("Error at %d in %s: invalid array index %d\n",
__LINE__, __FILE__, idx);
}

(For this strategy to work, error() must abend the program.)
 
M

Mark McIntyre

Keith Thompson said:


How so? Surely it will lead to incorrect results?

Not all programmes have incorrect results, and not all programmes have
results that can be trivially identified as incorrect. Consider a
random-number generator, or a driver for a TV display. One duff byte
would translate into a handful of incorrectly coloured pixels in a
matrix of a few million.
I disagree. Your clue is that the results are incorrect.

And your clue was that the results were quietly incorrect.
From there, it's
just a matter of finding out why. In my experience, this is a lot easier
when the results are reproducible.

Sure, but what about when you can't even spot the bug because your
default value is either also a valid result, or because it produces
unnoticeable problems except perhaps in very rare circumstances?
Yes, but I see a lot of "might"s in there. If you could guarantee this
behaviour, I'd be a lot happier.

Agreed. However with the variable initialised, its you can guarantee
the compiler *wont* warn you.

I'm agnostic. I tend to initialise variables which have a sensible and
useful initialiser, or ones where I need to be completely certain the
app doesn't fail, and where printing a blank line is better than
crashing.
If so, then that's a failure of one's testing process,

Sure. Show me the testing process that exhaustively tests every
possible circumstance, and I'll show you a perpetual motion machine.

--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
M

Mark McIntyre

Yes, incorrect results can be arbitrarily subtle and difficult
to detect. Does that mean we're allowed to produce incorrect results, or
not?

of course not. You seem to be deliberately missing the point.
If we're /not/ allowed to produce incorrect results, then our testing
had better be able to spot arbitrarily subtle output errors.

This is all true, but not germane to the topic. Initialising all
variables does *not* assist you in removing all possible errors from
your code. It merely makes the errors deterministic, while at the same
time removing a potentially helpful compiler warning. Knowing that
your programme repeatably prints two blanks at bytes 12345678 and
4557868 of a 1 trillion byte file does not necessarily assist much
with debugging. Having your copmiler complain that you forgot to
initialise X may.

Personally I'm agnostic in this debate. I initialise when I need to, I
don't waste effort when I don.t need to. IMHO anyone who initialises
loop variables is being anal.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
R

Richard Heathfield

Mark McIntyre said:

Sure, but what about when you can't even spot the bug because your
default value is either also a valid result,

That depends on what you mean by "valid". If the result is correct, there is
no problem. If the result is incorrect, there is a problem, and it should
be reasonably easy to trace it.
or because it produces
unnoticeable problems except perhaps in very rare circumstances?

That is precisely the behaviour that caused me to opt for blanket
initialisation in the first place. On two (widely separate) occasions when
I *didn't* initialise objects, the result was a problem which only
manifested itself on certain rare occasions. And no, the compiler didn't
warn me that those objects were being used without having valid values.
Agreed. However with the variable initialised, its you can guarantee
the compiler *wont* warn you.

But you can also guarantee deterministic behaviour, which is easier to
debug. And since the compiler isn't guaranteed to warn me, I'll take such
guarantees as I can find.
I'm agnostic. I tend to initialise variables which have a sensible and
useful initialiser, or ones where I need to be completely certain the
app doesn't fail, and where printing a blank line is better than
crashing.

Well, I can agree with that, given that I always need to be completely
certain that the app doesn't fail. (I'm not saying I *am* always certain of
that! I'm not claiming perfection here. But I always *need* to be certain.)
Sure. Show me the testing process that exhaustively tests every
possible circumstance, and I'll show you a perpetual motion machine.

....or a sufficiently trivial program, or an insufficiently powerful
computer. :)
 
R

Richard Heathfield

Mark McIntyre said:
of course not. You seem to be deliberately missing the point.

No, I can *see* your point. I even agree that your reasoning is not without
merit. I just don't agree that it's the only sensible strategy.
This is all true, but not germane to the topic. Initialising all
variables does *not* assist you in removing all possible errors from
your code.

I have found the opposite to be true...
It merely makes the errors deterministic,

....and that's why.
while at the same
time removing a potentially helpful compiler warning.

....which is not guaranteed to be provided.
Knowing that
your programme repeatably prints two blanks at bytes 12345678 and
4557868 of a 1 trillion byte file does not necessarily assist much
with debugging.

I disagree. It's a starting point, and it shouldn't take long to duplicate
the error on a much smaller data set.
Having your copmiler complain that you forgot to initialise X may.

Of course! But not having it complain because it doesn't do that kind of
complaint is rather less helpful. If using an indeterminate value were a
constraint violation, I'd be much happier to leave objects uninitialised
until the proper value is known - but it isn't a constraint violation, and
can't be, so I'm not.
 
I

Ian Collins

Yevgen said:
Thanks for catching this. With -O3 it inlines func(), and it does
warn if you use 'if (0) foo = 8'. Code below doesn't:

#include <stdio.h>

int func (void);

int main (void)
{
int foo;

if (func ())
foo = 8;

printf ("%d\n", foo);
return 0;
}
lint -Nlevel x.c

name used but not defined
func x.c(9)

function returns value which is always ignored
printf

possible use before set
foo defined at x.c(7)
 
Y

Yevgen Muntyan

Ian said:
lint -Nlevel x.c

name used but not defined
func x.c(9)

function returns value which is always ignored
printf

possible use before set
foo defined at x.c(7)

Well, your lint didn't break when gcc-4.1.2 was released. Can
you guarantee it won't break in future? Or can you guarantee
that it doesn't miss silly bugs right now? It's not really
a question about what tool is nicer here; point was that thinking
like "this code is fine because lint didn't find anything suspicious"
is wrong. Of course tools which check code are useful and needed,
any help from any source is good, absolutely.

"used but not defined" is interesting, by the way. What's wrong
with that 'func'?

Regards,
Yevgen
 
I

Ian Collins

Yevgen said:
Well, your lint didn't break when gcc-4.1.2 was released. Can
you guarantee it won't break in future? Or can you guarantee
that it doesn't miss silly bugs right now? It's not really
a question about what tool is nicer here; point was that thinking
like "this code is fine because lint didn't find anything suspicious"
is wrong. Of course tools which check code are useful and needed,
any help from any source is good, absolutely.
I just use lint as a sweeper in builds rather than rely on it to catch
silly bugs. As I develop test first, the chances of me having an unused
variable are slim (if it isn't required to pass a test, it won't be there).

Bugs are best caught by a combination of coding, development and testing
practices, not by one in isolation.
"used but not defined" is interesting, by the way. What's wrong
with that 'func'?
I was running lint in catch all mode. This lint can work with multiple
source files, so warnings such as this are useful. They can also be
turned off.
 
F

Flash Gordon

Ian Collins wrote, On 25/01/07 19:23:
lint -Nlevel x.c

name used but not defined
func x.c(9)

A stupid warning since it is obviously intended to be defined in another
source file. Of course, the declaration would be better in a header.
function returns value which is always ignored
printf

Not exactly the most helpful warning since one often wants to ignore
return values and casting to void just makes the code harder to read due
to increased clutter.
possible use before set
foo defined at x.c(7)

OK, so one warning in three is useful. Not a high hit rate. I'm sure you
can tune it better so it only gives useful warnings.
 
I

Ian Collins

Flash said:
Ian Collins wrote, On 25/01/07 19:23:


A stupid warning since it is obviously intended to be defined in another
source file. Of course, the declaration would be better in a header.
As I said, lint can work with multiple source files.
Not exactly the most helpful warning since one often wants to ignore
return values and casting to void just makes the code harder to read due
to increased clutter.
It can be filtered, I used the catch all mode.
OK, so one warning in three is useful. Not a high hit rate. I'm sure you
can tune it better so it only gives useful warnings.

Indeed.
 
M

Mark McIntyre

Mark McIntyre said:


That depends on what you mean by "valid". If the result is correct, there is
no problem. If the result is incorrect, there is a problem, and it should
be reasonably easy to trace it.

Rolls on floor laughing. If I had a dollar for every time someone said
"oh, that bug ought to be easy to find".... :). And I once worked on
code written by a guy so careful he checked every divisor for zero,
initialised every variable, never used the str... functions...

But you can also guarantee deterministic behaviour, which is easier to
debug.

You're incorrect here. There are times when its easier to diagnose,
and there are times when its not. I've already given some trivial
examples of the latter.
And since the compiler isn't guaranteed to warn me,

Thats a QoI issue. If your compiler doesn't warn you, switch compilers
or employers, or programme defensively by all means. But don't tailor
all of life to the worst-case scenario.
Well, I can agree with that, given that I always need to be completely
certain that the app doesn't fail.

I sincerely doubt you *always* need to be certain. However I also
think we have a dichotomy of understanding of the word "fail". For you
it seems to mean "produce any result at all, even a benign one, which
wasn't completely predictable by the inputs".
(I'm not saying I *am* always certain of
that! I'm not claiming perfection here. But I always *need* to be certain.)

Mhm, but emotional desires should be separated from programming
actualities ! :)
...or a sufficiently trivial program, or an insufficiently powerful
computer. :)

Only one of those suffices.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
M

Mark McIntyre

Mark McIntyre said:


I have found the opposite to be true...


...and that's why.

This is the crux. Your experience is not an exemplar.
...which is not guaranteed to be provided.

Again, this is merely a QoI issue.

--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
K

Keith Thompson

Flash Gordon said:
Ian Collins wrote, On 25/01/07 19:23:

A stupid warning since it is obviously intended to be defined in
another source file. Of course, the declaration would be better in a
header.

Ian wrote in a later followup that he was using lint in a mode that
works with multiple source files. Normally, I suppose you'd feed it
the file contaiing the definition of func(), and the warning would go
away.
Not exactly the most helpful warning since one often wants to ignore
return values and casting to void just makes the code harder to read
due to increased clutter.

You often want to ignore return values, but not always. In this case,
ignoring the result of printf() means you don't catch output errors,
so even here I'm not convinced the warning is invalid. But for a
function other than printf, ignoring the result might be a serious
error. I don't necessarily expect lint to know the difference (though
I suppose it could).
OK, so one warning in three is useful. Not a high hit rate. I'm sure
you can tune it better so it only gives useful warnings.

Or you can wade through the warnings and decide for yourself which
ones to ignore.
 
K

Keith Thompson

Richard Heathfield said:
Mark McIntyre said:


No, I can *see* your point. I even agree that your reasoning is not without
merit. I just don't agree that it's the only sensible strategy.
[...]

I think we're in more agreement than that last paragraph implies. I
won't try to speak for Mark, but I think you and I are both making the
same point, that it's not the only sensible strategy. The fact that
we're not talking about the same strategy doesn't imply that we
disagree; neither strategy is the only sensible one.
 
C

CBFalconer

Keith said:
Ian wrote in a later followup that he was using lint in a mode
that works with multiple source files. Normally, I suppose you'd
feed it the file contaiing the definition of func(), and the
warning would go away.

Nothing stupid about the warning. If it is external to the file it
should appear in the linking header file for the other module.
Every compilation has to stand on its own.

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
 
C

CBFalconer

Mark said:
.... snip ...


You're incorrect here. There are times when its easier to
diagnose, and there are times when its not. I've already given
some trivial examples of the latter.

I'm sticking with the 'it depends' crowd, with a bias to no
initializing, (less is more) and I am terminally lazy. However,
here's a technique I have used in the past, which depends on
no-initializing. It requires a system where you have control over
the initial content of the loading space. Easily done in the days
of CP/M and DDT, or MSDOS and DEBUG.

Fill the load space with something known. Load the program, run
it, and capture the output.

Fill the load space with something else. Repeat the load, run,
capture.

Compare the captured outputs. If different, there is an insect.
If same, it depends.

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
 
F

Flash Gordon

Keith Thompson wrote, On 25/01/07 23:01:
You often want to ignore return values, but not always. In this case,
ignoring the result of printf() means you don't catch output errors,
so even here I'm not convinced the warning is invalid.

In a larger program you might check the error status later, or you might
decide it is not worth ever catching.
> But for a
function other than printf, ignoring the result might be a serious
error.

Or might be even less important. For example ignoring the return value
of strcat.
> I don't necessarily expect lint to know the difference (though
I suppose it could).
Indeed.


Or you can wade through the warnings and decide for yourself which
ones to ignore.

True, although my experience is that if there are more than a very few
warnings people tend to drown in them rather than wade through them and
find the important ones even if they are *all* important. ;-)

As you say in another post, there is more than one sensible strategy. I
agree that lint can be a useful tool though.
 
K

Keith Thompson

CBFalconer said:
Nothing stupid about the warning. If it is external to the file it
should appear in the linking header file for the other module.
Every compilation has to stand on its own.

What is a "linking header file"?

For reference, here's an unmercifully abbreviated version of the
source file in question:

[...]
int func (void);

int main (void)
{
[...]
if (func ())
[...]
}

There is a declaration (but not a definition) for the function "func".
It doesn't matter to the compiler whether this declaration appears in
an included header file, or in the source file itself.

Now if you're saying that lint should warn about the fact that the
declaration appears in the source file rather than in an included
header (something that, as I said, the compiler doesn't care about,
but it is a valid style point), then I can see your point. But I
don't *think* that's what's happening here.

If it is, then you're right, I'm wrong, and I'm mildly impressed that
lint is this clever.

(BTW, I don't think there's a single tool called "lint"; there are
many different versions floating around.)
 

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

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,135
Latest member
VeronaShap
Top