Initialising Variables

C

CBFalconer

Keith said:
.... snip ...
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.

I'm sure we really agree. If there is an external file defining
func, it should have an associated header file, and that should
have been #included. Granted, that isn't enforced, but failure to
do so is pure sloppy. The local prototype, as in your abbreviated
source, is only proper if the function is defined in that file,
because it can't be #included in the actual definition file.

If Jacob really wants to improve C, he could work on enforcing
these second nature conventions. Especially since his users
probably tend to be newbies, who are especially likely to be
bitten.

--
<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
 
R

Richard Heathfield

Mark McIntyre said:
Rolls on floor laughing.

Do you find it easier to laugh at your correspondents than to think about
what they are actually saying? You talk a lot about how people like Jacob
Navia should avoid "rudeness". Have you ever considered taking your own
advice?
If I had a dollar for every time someone said
"oh, that bug ought to be easy to find".... :).

I am not denying the existence of hard-to-find bugs. I am, however, saying
that bugs are easier to find if they stand still.
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...

So? I've worked on lots of code written by folk who don't bother with
blanket initialisation. What makes you think this is relevant to the
discussion?
You're incorrect here.

I must disagree. I've debugged deterministic, and I've debugged
non-deterministic, and deterministic is easier.
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.

No, you haven't done any such thing. What you've shown is that you'd rather
rely on the (maybe very remote) possibility of a crash than on solid
engineering principles.

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.

But tailoring your code to the worst-case scenario /is/ defensive
programming.
I sincerely doubt you *always* need to be certain.

You mean you don't? Do your users know this?
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".

That's close to what *I* would mean by "fail" (modulo hardware errors and
resource acquisition failures). But I presumed you meant "...need to be
completely certain that the app doesn't crash" - so I was applying a much
less strict interpretation to your statement than I would to myself, and
yet you seem to think it's even okay for apps to crash sometimes.
Mhm, but emotional desires should be separated from programming
actualities ! :)

What has emotion got to do with anything? No client ever said to me "write a
program which only works sometimes".
 
R

Richard Heathfield

Mark McIntyre said:
This is the crux. Your experience is not an exemplar.

It is, however, an example (and in that sense /is/ an exemplar, according to
one of the definitions of that word).

Again, this is merely a QoI issue.

Yes, good compilers will warn against *some* instances of using
indeterminate values of objects, but it is trivial to demonstrate instances
where this is not possible except with an unacceptably high level of false
positives, and I did so only a short time ago.
 
M

Mark McIntyre

Mark McIntyre said:


Do you find it easier to laugh at your correspondents than to think about
what they are actually saying?

As it happens, I really did find that extremely amusing and I really
do wish I had fifty pee for every time someone said that to me. if
you choose to take umbrage at the fact that you made what I consider
to be a foolishly humorous remark, thats your lookout.
You talk a lot about how people like Jacob
Navia should avoid "rudeness". Have you ever considered taking your own
advice?

Et primus lapidus jaceret?
I am not denying the existence of hard-to-find bugs. I am, however, saying
that bugs are easier to find if they stand still.

And I'm disagreeing. if you'd said that *some* bugs are easier, I'd
have agreed.
What makes you think this is relevant to the
discussion?

Your point was that somehow, blanket initialisation made it easier to
find bugs. I have direct experience to the contrary. Mine's not an
exemplar either, but it is an example.

The bugs in question were _due_ to the initialisers which were badly
chosen, or hid logic errors such as failing to set X in one branch of
a complex conditional. By initialising everything, these bugs were
invisible.
No, you haven't done any such thing.

I have. Feel free to read back through the thread and find them.
What you've shown is that you'd rather
rely on the (maybe very remote) possibility of a crash than on solid
engineering principles.

For what its worth, that remark is priggish and pompous.
--
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

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.

I'm on the same boat - neither is the only sensible strategy. I'd go a
tad further though, and say that you need to tailor your strategy for
the job in hand and the conditions you face, rather than take a
one-size-fits-all approach.
--
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
 
I

Ian Collins

Mark said:
I'm on the same boat - neither is the only sensible strategy. I'd go a
tad further though, and say that you need to tailor your strategy for
the job in hand and the conditions you face, rather than take a
one-size-fits-all approach.

I agree, my preferences are:

1) Keep functions short, which both reduces the chance of something
going uninitialised and makes it easy to spot if it is.

2) Use C99's declare when required if you can.

3) Use highest level compiler warnings or lint.

4) Code test first - if you don't need it, it won't be declared.

5) Compile (and if possible, run tests) with more than one compiler.
 
C

Chris Torek

[R.H. notes that he tends to initialize all variables at declaration,
for various reasons that I have snipped.]
santosh said:

I can see why you ask, of course. Curiously, however, the answer
is "no". That probably sounds inconsistent. In fact, there are no
two ways about it - it *is* inconsistent!
[snippage]

So it's a trade-off between debuggability and performance. We all
make this trade-off at some point - we just draw our lines at
different places in the sand, that's all.

In that case, what is your take on, e.g.:

int f(void) {
struct very_large_struct s /* optional: = { 0 } */ ;
T array[VERY_LARGE_N] /* optional: = { 0 } */ ;
... code ...
}

(where T is some type name)?

(I prefer to *not* insert "obviously unnecessary" initializations,
except where the compiler is not smart enough to know that they
are in fact unnecessary and therefore produces warnings. In that
case, I tend to comment the initializer with something like
"initialized just to silence a warning". If I think that, for
whatever reason, it is *not* obvious that the initialization is
unnecessary, I will tend to insert one.)
 
R

Richard Heathfield

Chris Torek said:
Richard Heathfield said:
So it's a trade-off between debuggability and performance. We all
make this trade-off at some point - we just draw our lines at
different places in the sand, that's all.

In that case, what is your take on, e.g.:

int f(void) {
struct very_large_struct s /* optional: = { 0 } */ ;
T array[VERY_LARGE_N] /* optional: = { 0 } */ ;
... code ...
}

(where T is some type name)?

If they're large enough that initialising them is a performance issue, they
must be fairly colossal, and I try to avoid creating fairly colossal auto
objects, for completely different reasons.
(I prefer to *not* insert "obviously unnecessary" initializations,
except where the compiler is not smart enough to know that they
are in fact unnecessary and therefore produces warnings. In that
case, I tend to comment the initializer with something like
"initialized just to silence a warning". If I think that, for
whatever reason, it is *not* obvious that the initialization is
unnecessary, I will tend to insert one.)

Perfectly sensible strategy.
 
S

santosh

Richard said:
Chris Torek said:
<snip discussion on initialisation>

As an aside, does anyone know the rationale for initialising global
objects to zero, if they're left uninitialised in program source? Is
this another artifact of C's evolution or is there a sound reason to
automatically zero out global memory over and above objects of lesser
scope?

Why wasn't initialisation of global memory left in programmer's hands,
as is the case for auto objects? After all, using an uninitialised
auto object can just as easily lead to undefined behaviour as an
uninitialised global object.

I'm just puzzled about the lack of symmetry between global and auto
objects, as regards this issue.
 
R

Richard Tobin

santosh said:
As an aside, does anyone know the rationale for initialising global
objects to zero, if they're left uninitialised in program source? Is
this another artifact of C's evolution or is there a sound reason to
automatically zero out global memory over and above objects of lesser
scope?

It's naturally implementable on common machines and operating systems:
you just keep the static or global variable in an area of memory
initialised to zero by the operating system. You can't do that for
automatic variables stored on a stack, because the same memory is
reused for different variables (or different instances of the same
variable) during a program run. When auto variables are explicitly
initialised, this typically requires code to be generated to do the
initialisation.

As I've mentioned before, bugs were found in many standard unix
programs when Sun introduced dynamic linking, because previously the
stack had always happened to be full of zeroes when main() was called,
and programs were inadvertently relying on some automatic variables
being initialised to zero. Dynamic linking required the running of
code before main(), which left random data on the stack.

-- Richard
 
C

CBFalconer

santosh said:
.... snip ...

Why wasn't initialisation of global memory left in programmer's
hands, as is the case for auto objects? After all, using an
uninitialised auto object can just as easily lead to undefined
behaviour as an uninitialised global object.

It is. If you do nothing, all static storage is initialized to all
bytes 0. If you write initialization statements for them it
increases the size of the load module, because it includes the
initialized values. The automatic initialization is done by one
call to memset, or the equivalent, at program startup.

One very important result is that the program can tell when a
sub-system has been initialized, by having the initialization
routine leaving a non-zero in some static flag.
 
K

Keith Thompson

CBFalconer said:
It is. If you do nothing, all static storage is initialized to all
bytes 0.
[snip]

Static objects with no explicit initialization are initialized to zero
converted to the appropriate type (recursively for arrays and
structures). This happens to be all-bytes-zero on many systems, but
it's not guaranteed. If a null pointer's representation, is, say,
0xFFFFFFFF, then a static pointer object will be initialized to
0xFFFFFFFF.

(I think static unions are initialized according to the first member.)
 
I

Ian Collins

Keith said:
CBFalconer said:
santosh wrote:

... snip ...


It is. If you do nothing, all static storage is initialized to all
bytes 0.

[snip]

Static objects with no explicit initialization are initialized to zero
converted to the appropriate type (recursively for arrays and
structures). This happens to be all-bytes-zero on many systems, but
it's not guaranteed. If a null pointer's representation, is, say,
0xFFFFFFFF, then a static pointer object will be initialized to
0xFFFFFFFF.
I wonder how many systems do anything other than place all statics in
one block and zero it, without regard for type.
 
R

Richard Tobin

I wonder how many systems do anything other than place all statics in
one block and zero it, without regard for type.

That is a good reason for having NULL pointers be all-bits-zero.

-- Richard
 
K

Keith Thompson

Ian Collins said:
Keith Thompson wrote: [...]
Static objects with no explicit initialization are initialized to zero
converted to the appropriate type (recursively for arrays and
structures). This happens to be all-bytes-zero on many systems, but
it's not guaranteed. If a null pointer's representation, is, say,
0xFFFFFFFF, then a static pointer object will be initialized to
0xFFFFFFFF.
I wonder how many systems do anything other than place all statics in
one block and zero it, without regard for type.

Probably not many. If zero for all types happens to be represented as
all-bits-zero (as is the case on most systems), there's no point in
doing anything else.
 
D

Dik T. Winter

> In article <[email protected]>,

>
> It's naturally implementable on common machines and operating systems:
> you just keep the static or global variable in an area of memory
> initialised to zero by the operating system.

And on some systems that does not work (when 0.0 is not all bits zero, or
the null pointer is not all bits zero). The reason is simpler. Even
very old loaders allowed the allocation of blocks of data during the
load process that were (partly or completely) initialised. And if
only part was initialised, the remainder was in general set to zero.
This is an inheritance from Fortran with common blocks and BLOCK DATA.
Zeroing the memory (when not initialised) makes sense, otherwise you
could find in those parts of memory data from previous programs that
did run using the same memory. In general, when (during stack increases
or heap request) new memory is requested from the OS, it is also
initialised to zero, for the same reasons. But as, on most systems,
freeing memory or reducing the stack size does *not* return the memory
to the OS, when you re-increase the memory or again get memory from the
heap, there is no reason to initialise it at all, the data in there is
from the same program.
 
D

Dik T. Winter

> I wonder how many systems do anything other than place all statics in
> one block and zero it, without regard for type.

I have used quite a few. If they had done what you suggest they would not
have conformed to the standard. (0.0 not all bytes 0, null pointer not all
bytes 0, yes, I have used both kinds.)
 
D

Dik T. Winter

>
> That is a good reason for having NULL pointers be all-bits-zero.

Why? Most loaders allow the preinitialisation of data blocks with all
zero bytes except for some words, that are initialised to something
else. The load module will be slightly larger, but that is peanuts.
 
C

Christopher Layne

Keith said:
Probably not many. If zero for all types happens to be represented as
all-bits-zero (as is the case on most systems), there's no point in
doing anything else.

As an aside, I would think it even less preferable these days with the ability
for modern operating systems to take advantage of sparse file handling.
 

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
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top