To use global variables or no????

C

CBFalconer

John said:
I might be misunderstanding you, but don't some types of function
need to keep "static" state information? E.g. functions like malloc?

Not malloc. strtok() yes.

--
"Our enemies are innovative and resourceful, and so are we.
They never stop thinking about new ways to harm our country
and our people, and neither do we." -- G. W. Bush.
"The people can always be brought to the bidding of the
leaders. All you have to do is tell them they are being
attacked and denounce the pacifists for lack of patriotism
and exposing the country to danger. It works the same way
in any country." --Hermann Goering.
 
D

Dann Corbit

Al Balmer said:
I may also be misunderstanding, but I often find static objects useful
when writing access functions for opaque types.

Static data objects are useful when there is no better way to achieve the
same desired result (typically, persistent state information that needs
exactly one copy for the duration of program execution). On the other hand,
they (static data objects) often cause a lot more problems than they solve.
A typical example is strtok(). Because strtok() maintains static
information, calling strtok() from two different locations in your program
can lead to subtle bugs. The strtok_r() design is a much better design for
that reason.
 
E

Eric Sosman

CBFalconer wrote On 06/06/06 14:54,:
Not malloc. strtok() yes.

How can malloc() recycle memory released by free()
if the two functions don't share at least one static
variable?

Similar concerns apply elsewhere in the library:
exit() must call (and hence find) all the functions
registered with atexit(), and must close (and hence
find) all the streams opened with fopen() and not yet
closed with fclose(). fflush(NULL) must find all the
open streams whose last operation was an output. The
<locale.h> mechanisms can change the behavior of the
<ctype.h> functions. And so on, and so on ... How
are any of these interactions to be handled without
static data?

Of course, non-C languages might provide means
other than static variables for sharing persistent data
across "function" invocations. And it's well known that
the C library is not required to be written in C -- but
that's a long way from requiring that it *not* be written
in C!
 
D

Dann Corbit

Eric Sosman said:
CBFalconer wrote On 06/06/06 14:54,:

How can malloc() recycle memory released by free()
if the two functions don't share at least one static
variable?

Similar concerns apply elsewhere in the library:
exit() must call (and hence find) all the functions
registered with atexit(), and must close (and hence
find) all the streams opened with fopen() and not yet
closed with fclose(). fflush(NULL) must find all the
open streams whose last operation was an output. The
<locale.h> mechanisms can change the behavior of the
<ctype.h> functions. And so on, and so on ... How
are any of these interactions to be handled without
static data?

Why not use allocated memory? (Sounds a bit recursive with malloc(), but we
can use an OS-level allocator.).

E.g. When a file is opened, add the file handle along with any other needed
data to a skiplist.
When the file is closed or the program exits, remove the object from the
skiplist after performing all necessary cleanup operations.

Similarly for all of the others. I would submit that extern and static are
never really needed. We just use them because we're lazy.

For a single instance variable, I could make it an auto at main() level and
pass it as a parameter. But I may feel lazy and make it static or even
extern so I don't have to add the argument to all of the functions that need
it.

I guess that global variables are not needed ever. But I probably just fail
to use proper vision in thinking the problem through.
 
E

Eric Sosman

Dann Corbit wrote On 06/06/06 17:02,:
Why not use allocated memory? (Sounds a bit recursive with malloc(), but we
can use an OS-level allocator.).

E.g. When a file is opened, add the file handle along with any other needed
data to a skiplist.
When the file is closed or the program exits, remove the object from the
skiplist after performing all necessary cleanup operations.

Similarly for all of the others. I would submit that extern and static are
never really needed. We just use them because we're lazy.

extern, no. But I don't see a way to do without static.
The skip list you mention, for instance: How do all of the
functions fopen(), fclose(), fflush(), and exit() manage to
find the list's first node? Even if they call __find_list(void)
to locate it, where does __find_list() store the pointer if
not in a static variable of its own?

Even if we make all things that are usually static reside
in a dynamically-allocated data structure whose address is
obtained from some magical system call, how would this be an
improvement over using global variables? That is, what is
the fundamental difference between

extern int global;
void f(void) {
global = 42;
}

and

void f(void) {
ProgramContext *ctx = __get_program_context_pointer();
ctx->global = 42;
}

? Wouldn't this just substitute an explicit pointer for the
implicit "pointer" expressed by the values in MMU registers?

Maybe all this confusion stems from different understandings
of the loose term "global." I sure hope so: it makes me nervous
when Falconer and Corbit both disagree with me ... I'm reminded
(uncomfortably) of Pauli's hubris in remarking "What Professor
Einstein has just said is not so stupid."
 
D

Dann Corbit

Eric Sosman said:
Dann Corbit wrote On 06/06/06 17:02,:

extern, no. But I don't see a way to do without static.
The skip list you mention, for instance: How do all of the
functions fopen(), fclose(), fflush(), and exit() manage to
find the list's first node? Even if they call __find_list(void)
to locate it, where does __find_list() store the pointer if
not in a static variable of its own?

Even if we make all things that are usually static reside
in a dynamically-allocated data structure whose address is
obtained from some magical system call, how would this be an
improvement over using global variables? That is, what is
the fundamental difference between

extern int global;
void f(void) {
global = 42;
}

and

void f(void) {
ProgramContext *ctx = __get_program_context_pointer();
ctx->global = 42;
}

? Wouldn't this just substitute an explicit pointer for the
implicit "pointer" expressed by the values in MMU registers?

Maybe all this confusion stems from different understandings
of the loose term "global." I sure hope so: it makes me nervous
when Falconer and Corbit both disagree with me ... I'm reminded
(uncomfortably) of Pauli's hubris in remarking "What Professor
Einstein has just said is not so stupid."

I'm not necessarily disagreeing. Although dynamic allocation could replace
the use of static instance variables, it is not necessarily a better
solution. There is a difference between "You don't have to do it this way"
and "It's not as good to do it this way."

Almost all of the programming that I do is mutlithreaded and multiprocess
and so I tend to think of anything global or writable static as evil.
However, I imagine that sometimes they may be a necessary evil. There are
ways to avoid using static and extern but the solutions may be worse than
the problem they are trying to cure.

For instance, my assertion to use dynamic allocation introduces a new
problem:
How to handle resource allocation failure.
That problem does not exist with a pre-defined instance variable which we
know exists if the compile succeeds.

I almost never use global variables. But my abhorrence is from all the
effort I expend in avoiding or removing them, no doubt.
 
C

CBFalconer

Eric said:
CBFalconer wrote On 06/06/06 14:54,:

How can malloc() recycle memory released by free()
if the two functions don't share at least one static
variable?

True enough. I was guilty of sloppy thinking. However, in
justification, I think of the malloc system as a separate entity,
mediating between the fixed resources (as supplied via sbrk) and
the running program(s). As long as suitable synchronizaton steps
are taken to avoid fouling of variables, the system can be
re-entrant and thread safe.

However, for one thread (which is all we officially have in C)
there is no problem mixing calls to the malloc family. strtok is
another matter entirely.

--
"Our enemies are innovative and resourceful, and so are we.
They never stop thinking about new ways to harm our country
and our people, and neither do we." -- G. W. Bush.
"The people can always be brought to the bidding of the
leaders. All you have to do is tell them they are being
attacked and denounce the pacifists for lack of patriotism
and exposing the country to danger. It works the same way
in any country." --Hermann Goering.
 
R

Richard Heathfield

John Devereux said:
I might be misunderstanding you, but don't some types of function need
to keep "static" state information? E.g. functions like malloc?

My apologies, John, for not making myself clear enough. I wasn't thinking
about implementing the standard library, but about more general programming
tasks. It would be impossible, I think, to write strtok in C without using
a static duration object. And yes, the mm subsystem needs to keep some
state too. (Actually, strtok /wouldn't/ need to keep state if they'd
thought about it a bit more; it appears to have been conceived in the days
when "get it done and never mind how as long as it's quick" was a view more
widely held than it is today.)

The two uses I had in mind are:

(a) you have a whole bunch of configuration information, which is needed by
just about every function in your shiny new library (for example, the
current default size for dynamic buffers, that sort of thing), and you
don't wish to impose upon your users the burden of passing a
pointer-to-config-info to absolutely every function in the library. That's
the use I don't like, but I can live with.
(b) signal handling. Just about the only thing you can do portably in a
signal handler is change the value of a sig_atomic_t object. And there is
no mechanism in the signal handler prototype to handle sig_atomic_t
objects! AAARGH! So you don't have much choice but to bang in a static at
file scope.
 
J

John Devereux

Richard Heathfield said:
John Devereux said:


My apologies, John, for not making myself clear enough.

That's OK. I could see you were in riddle mode :)

I wasn't thinking about implementing the standard library, but about
more general programming tasks. It would be impossible, I think, to
write strtok in C without using a static duration object. And yes,
the mm subsystem needs to keep some state too. (Actually, strtok
/wouldn't/ need to keep state if they'd thought about it a bit more;
it appears to have been conceived in the days when "get it done and
never mind how as long as it's quick" was a view more widely held
than it is today.)

The two uses I had in mind are:

(a) you have a whole bunch of configuration information, which is needed by
just about every function in your shiny new library (for example, the
current default size for dynamic buffers, that sort of thing), and you
don't wish to impose upon your users the burden of passing a
pointer-to-config-info to absolutely every function in the library. That's
the use I don't like, but I can live with.

This is what I end up using them for. I have a global structure, say
"settings". Lots of functions need to be able to access the members,
in lots of different modules. Most functions only need access to a
couple of members each. But they need to be in a single big structure
so that menu and serialisation code can get at them conveniently. I
can pass around pointers to that structure, and have the structure
declared "static" somewhere at file scope instead of "extern". But I
am not really sure I gain anything by doing that!

I keep thinking there must be a better way but have not found one
yet...
 
K

Keith Thompson

John Devereux said:
I might be misunderstanding you, but don't some types of function need
to keep "static" state information? E.g. functions like malloc?

The malloc, calloc, realloc, and free functions almost certainly need
to keep some state information (with static storage duration) that
they share. (That's assuming they're implemented in C, which isn't
required.)
 
A

av

Dann Corbit wrote On 06/06/06 17:02,:

extern, no. But I don't see a way to do without static.
The skip list you mention, for instance: How do all of the
functions fopen(), fclose(), fflush(), and exit() manage to
find the list's first node? Even if they call __find_list(void)
to locate it, where does __find_list() store the pointer if
not in a static variable of its own?

in assembly there is a BSS section that reserve memory in the stack
for its variable-data definitions. (if i remember well)
don't know if i can compile to object file an assembly file that has
all names in the BSS section and compile that obj-file with the C one
file that has routines that see the names in the assembly BSS section.
Don't know if it is right...
 
G

Gordon Burditt

Why not use allocated memory? (Sounds a bit recursive with malloc(), but we
in assembly there is a BSS section that reserve memory in the stack
for its variable-data definitions. (if i remember well)

You don't. BSS reserves memory in the *NON*-stack (which is still
true for systems that have BSS but don't have a hardware stack) for
its variable-data definitions. What gets put there? uninitialized
non-auto, non-const variables. Static and extern.
don't know if i can compile to object file an assembly file that has
all names in the BSS section and compile that obj-file with the C one
file that has routines that see the names in the assembly BSS section.
Don't know if it is right...

It's possible, but the variables are exactly the types that some
people are declaring evil.

Gordon L. Burditt
 
R

Richard Bos

Dann Corbit said:
Why not use allocated memory? (Sounds a bit recursive with malloc(), but we
can use an OS-level allocator.).

E.g. When a file is opened, add the file handle along with any other needed
data to a skiplist.

How does malloc() remember where its skiplist starts?

Richard
 
D

Dann Corbit

Richard Bos said:
How does malloc() remember where its skiplist starts?

The malloc function puts a struct into a skiplist with an auto root node at
the scope of main (or even above it, since it is a compiler implemented
function). The struct in the skiplist stores the address, the size, and any
other relevant data. When someone calls free() the address is the key for
the skiplist. If the address is NULL, the function returns. Otherwise, the
address is searched for in the skiplist. If the address is found, the
memory is returned to the operating system.
 
G

Gordon Burditt

How does malloc() remember where its skiplist starts?
The malloc function puts a struct into a skiplist with an auto root node at
the scope of main (or even above it, since it is a compiler implemented
function).

If it's written in C, malloc() (or any other function) cannot put
a auto root node at a scope other than itself. C is not Pascal.
And ANSI C doesn't allow you to require passing the address of that
node to malloc(). And if the scope is that of malloc(), the value
goes away when malloc() returns.

You've still got a variable that is accessed by several different
functions that retains its value between calls. That sounds like
a non-auto, non-const variable (often called a "global") to me.
And it has the problems of one - being accessible from all over.
The struct in the skiplist stores the address, the size, and any
other relevant data.

Fine, *if* the skiplist can be found.
When someone calls free() the address is the key for
the skiplist.

So how does free() find the root node for the skiplist?
If the address is NULL, the function returns. Otherwise, the
address is searched for in the skiplist. If the address is found, the
memory is returned to the operating system.

Gordon L. Burditt
 
R

Richard Bos

Dann Corbit said:
The malloc function puts a struct into a skiplist with an auto root node at
the scope of main

It can't do that, since free() must be callable from atexit()-functions.
What's more, that skiplist must have (what amounts to) a declaration in
scope for both malloc() and free().
(or even above it, since it is a compiler implemented function).

It _can_ do this, but I would argue that that is the same thing as
creating a static object - perhaps in different terms, but the same
thing functionally.

Richard
 
D

Dann Corbit

Gordon Burditt said:
If it's written in C, malloc() (or any other function) cannot put
a auto root node at a scope other than itself. C is not Pascal.
And ANSI C doesn't allow you to require passing the address of that
node to malloc(). And if the scope is that of malloc(), the value
goes away when malloc() returns.

You've still got a variable that is accessed by several different
functions that retains its value between calls. That sounds like
a non-auto, non-const variable (often called a "global") to me.
And it has the problems of one - being accessible from all over.


Fine, *if* the skiplist can be found.


So how does free() find the root node for the skiplist?


Gordon L. Burditt

This is not a user-level operation. It is at the compiler vendor level. I
imagine something like this:

void magic_compiler_level_thing_over_main()
{
memory_node_t root;
allocator_initialization(root);
main(); /* malloc and free use the data structure created above */
}
 
G

Gordon Burditt

The malloc function puts a struct into a skiplist with an auto root node
This is not a user-level operation. It is at the compiler vendor level. I
imagine something like this:

void magic_compiler_level_thing_over_main()
{
memory_node_t root;
allocator_initialization(root);
main(); /* malloc and free use the data structure created above */
}

I'd expect the parameter passed to allocator_initialization to be &root,
not root, unless a memory_node_t is an array.

So where does allocator_initialization() store the root node (or
its address) so malloc() and free() can get to it? It sounds to
me liek allocator_initialization() is, among other things, sticking
a pointer value into a non-auto, non-const variable for later use
by malloc() or free(). (You could do it by having
allocator_initialization(NULL) return the address of the root node,
rather than have malloc() and free() access it directly. That still
requires allocator_initialization() to use a static.

Gordon L. Burditt
 

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
474,183
Messages
2,570,969
Members
47,524
Latest member
ecomwebdesign

Latest Threads

Top