The machine stack and the C language

F

Flash Gordon

jacob navia wrote, On 13/01/08 13:16:
James Kuyper wrote:


Because they have a stack OBVIOUSLY!

<snip>

OK, so you consider such implementations to have a stack. So why did you
claim that such implementations where not relevant to the topic of
detecting stack exhaustion? After all, such implementations *can* run
out of memory, so their stack (which you consider them to have) can
become exhausted. Also tell me how your suggested method of taking the
difference in addresses of variables declared in main and in the current
stack frame will give any clue as to stack usage on such an implementation.
 
S

Serve Lau

James Kuyper said:
I believe it has been. The people who are saying that a stack is not
required are not using the word "stack" in the abstract sense of a generic
LIFO allocation mechanism. They are using it in the more common, more
concrete sense of the word, to refer specifically to implementation of
LIFO semantics by use of a single large block of memory which grows and
shrinks only at one end.

Real implementations have been described in this thread that don't possess
such a stack, using activation records which are allocated and deallocated
from locations in memory that are not in any particular order. I don't see
any reason why such an implementation should have any trouble supporting
recursion.

As has been pointed out, that is merely a software stack implementation. I
was really hoping for something completely different from a stack mechanism.
I dont really care if C calls it a stack or automatics or something else,
I'm just trying to learn something new.
 
J

jacob navia

James said:
No. It would be more accurate to say:

"The standard says nothing about stacks as such. What the standard says
about the lifetime of variables with automatic storage duration makes it
natural to implement them using the LIFO semantics of the stack that is
a commonplace feature of many computer architectures. However, the
standard does not mandate the use of such a stack; it doesn't even
mandate the LIFO semantics of an abstract stack, as the term is
understood in computer science."


Yes it does

int fn(void)
{
int a = 6;
}

int main(void)
{
a = 78;

fn();
}

Before the call to fn() the value of "a" is 78.
During the call to fn it is 6.

After the call to fn, the value of a REVERTS to its previous value
The last value defined is the first that goes away.

Last in, first out (LIFO)

i.e. a stack!

You can call a cat a feline, a domestic animal, and all that
is true but it remains a cat.
Before you repeat that the claim, which has been frequently made in this
thread, that LIFO semantics are mandatory, please consider the following
implementation:

It allocates and deallocates space for activation records. It makes sure
that it always allocates a given record before the start of the lifetime
of the variables stored in that record, and it makes sure that it never
deallocates them until after the end of the lifetime of those
variables, but it does not always carry out the allocations and
deallocations in the same order as a LIFO mechanism would.

If variable "a" is not deallocated after the call to fn, this
would violate the standard and the C language of course!

The actual storage for that variable is never deallocated right away
in most implementations that use a hardware stack since
memory is allocated in pages, and remains allocated for some
indefinite time until the page is freed. That is why the following
works sometimes:

char *fn(void) { char a; return &a;}

If you experiment in different implementations of gcc, or other
compilers you will see that *sometimes* you can access the value
without crashing. But the fact that you can illegally access
some parts of the stack no longer valid doesn't mean there is
no stack. The same for your example.
What requirement of the C standard would such an implementation violate?

None since it uses a stack.

Besides you have yet to answer the question about a SINGLE example of
a real implementation that doesn't use a stack!
 
R

Richard Harter

Please stop repeating this falsehood. comp.std.c is for discussing
the language standard.

"Falsehood" is far too strong. "Infelicitous phrasing" would be
better, particularly since it is likely that English is not
Jacob's native tongue. He might have well meant that which you
more accurately phrased.

Be that as it may, the tendency of many usenet posters to cast
things in black and white is unfortunate and leads to an
unnecessarily abrasive venue of discourse. Some relish that sort
of brangling. I am quite convinced that you, however, are that.
 
H

Harald van Dijk

Yes it does

int a;
int g()
{
a = 8;
}
int fn(void)
{
int a = 6; g();
}

int main(void)
{
a = 78;

fn();
}

Before the call to fn() the value of "a" is 78. During the call to fn it
is 6.

The global object a is once set to 78, and once to 8 in the function I've
added. During the call to fn, it is not somehow set to 6. Rather, the
global object a cannot be accessed by name, and the local object a can
be. This is why g can access the global a even though the lifetime of the
local a hasn't ended.
After the call to fn, the value of a REVERTS to its previous value

No, the value of the global object a was never changed by fn, so there's
nothing to revert.
The
last value defined is the first that goes away.

Last in, first out (LIFO)

i.e. a stack!

In the simplest cases, sure.
You can call a cat a feline, a domestic animal, and all that is true but
it remains a cat.

If variable "a" is not deallocated after the call to fn, this would
violate the standard and the C language of course!

The local variable a does not have to be deallocated after the call to
fn. It merely ceases to be accessible by name, because it goes out of
scope. Its lifetime may be extended by the implementation for whatever
reason.
None since it uses a stack.

Even if it doesn't consistently release objects in the reverse order of
allocation?
 
J

James Kuyper

Richard said:
"Falsehood" is far too strong. "Infelicitous phrasing" would be
better, particularly since it is likely that English is not
Jacob's native tongue. He might have well meant that which you
more accurately phrased.

I think that's unlikely, from context. "language standard" would not fit
as a replacement for "standard language", when you consider then entire
message that the phrase occurred in.
 
R

Richard Harter

Richard said:
[...]
comp.std.c is for discussing the standard language.
Please stop repeating this falsehood. comp.std.c is for discussing
the language standard.

"Falsehood" is far too strong. "Infelicitous phrasing" would be
better, particularly since it is likely that English is not
Jacob's native tongue. He might have well meant that which you
more accurately phrased.

I think that's unlikely, from context. "language standard" would not fit
as a replacement for "standard language", when you consider then entire
message that the phrase occurred in.

"You might very well think that ...
 
J

James Kuyper

jacob said:
Yes it does

int fn(void)
{
int a = 6;
}

int main(void)
{
a = 78;

fn();
}

Before the call to fn() the value of "a" is 78.
During the call to fn it is 6.

After the call to fn, the value of a REVERTS to its previous value
The last value defined is the first that goes away.

Last in, first out (LIFO)

i.e. a stack!

The name 'a' refers to two different variables, depending upon context.
That's a matter of the scope of the variable name, not it's lifetime.
The compiler would probably use a stack to keep track of scopes at
compile time; that doesn't say anything about how memory is managed in
the running program at run time.

If your argument were valid, it would apply equally well even if both
variables were declared 'static'. I don't believe it's your intent to
claim that statically allocated memory also requires a stack mechanism.

The relevant issue is the lifetime of the variables, not their scope.
However, while the lifetime of the variable named 'a' in 'fn' has ended
when the function returns, there's nothing in the standard that requires
that the memory allocated for that variable be deallocated. Not when
it's lifetime ends, and not at any other time, either. The fact that
most implementations deallocate the space at the earliest possible time
is a matter of QoI, not a requirement of the standard.

If, for example, it's inconvenient on a given system to deallocate less
than 512 bytes, I could imagine an implementation which groups together
several functions requiring a total of less than 512 bytes of space for
all automatic variables. It allocates all of their activation records as
soon as any of those functions gets called, and it deallocates all of
their activation records at the same time, only when none of them are
associated with a currently executing function call.
If variable "a" is not deallocated after the call to fn, this
would violate the standard and the C language of course!

Citation please?

Of course, that's not actually a problem, even if you did have a
supporting citation. The implementation I describe could delay
deallocation of the 'a' in fn() until after the deallocation of the 'a'
in main(). That wouldn't violate the requirement you assert (without
citation), because that deallocation would still be, as specified,
"after the call to fn". It just wouldn't be immediately after.

For dynamically allocated memory, the standard says "The free function
causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation." There is no comparable requirement
for deallocation of automatic memory.
If you experiment in different implementations of gcc, or other
compilers you will see that *sometimes* you can access the value
without crashing. But the fact that you can illegally access
some parts of the stack no longer valid doesn't mean there is
no stack. The same for your example.

True. The relevant feature that makes this NOT a stack is the fact that
the memory has not necessarily been been deallocated yet. Whether or not
it can be accessed by illegal constructs is irrelevant. The fact that
the activation records might be allocated in the order (main, fn) and
deallocated in that same order is what makes this not a LIFO, and
therefore not, even in the most abstract sense, a stack.
None since it uses a stack.

Besides you have yet to answer the question about a SINGLE example of
a real implementation that doesn't use a stack!

Real systems have been described (but not, I think, identified) which
don't use a stack in the commonplace sense of that word. That you insist
on ignoring the fact that this is the commonplace sense of the word is a
problem I can't do anything about.

The fact is, most of the people coming to this newsgroup with a question
about the 'stack' have a question which does not depend upon the
abstract concept of a stack, but rather on the far more concrete and
commonplace idea of a contiguous stack which grows and shrinks only at
one end, and the misconception that the C standard requires such a stack.
 
K

Keith Thompson

jacob navia said:
Yes it does

int fn(void)
{
int a = 6;
}

int main(void)
{
a = 78;

fn();
}

It would be helpful if you'd use a valid program. You've declared an
object named ``a'' inside fn(). That object is not visible in main().

Presumably the ``a'' referred to in main() either is declared locally
to main(), or is declared outside any function. I'll assume you
intended it to be local to main(), i.e., that ``a = 78;'' should be
``int a = 78;''.
Before the call to fn() the value of "a" is 78.
During the call to fn it is 6.

After the call to fn, the value of a REVERTS to its previous value
The last value defined is the first that goes away.

The ``a'' declared in fn() and the ``a'' declared in main are two
different objects; the fact that they happen to have the same name is
not relevant. Nothing "reverts". The phrase ``the value of "a"''
means different things depending on the context.

[...]
If variable "a" is not deallocated after the call to fn, this
would violate the standard and the C language of course!
[...]

It depends on what you mean by "deallocated". It's not visible
outside fn(); that has nothing to do with deallocation.

Perhaps you could try to make your point more clearly.
 
K

Keith Thompson

"Falsehood" is far too strong. "Infelicitous phrasing" would be
better, particularly since it is likely that English is not
Jacob's native tongue. He might have well meant that which you
more accurately phrased.
[...]

I don't believe so. I believe that jacob meant exactly what he wrote,
that comp.std.c is for discussion of "the standard language". He's
made similar statements in the past. I stand by my use of the word
"falsehood", and I invite jacob to correct me if I've misunderstood
what he wrote.
 
R

Richard Harter

[email protected] (Richard Harter) said:
[...]
comp.std.c is for discussing the standard language.

Please stop repeating this falsehood. comp.std.c is for discussing
the language standard.

"Falsehood" is far too strong. "Infelicitous phrasing" would be
better, particularly since it is likely that English is not
Jacob's native tongue. He might have well meant that which you
more accurately phrased.
[...]

I don't believe so. I believe that jacob meant exactly what he wrote,
that comp.std.c is for discussion of "the standard language". He's
made similar statements in the past. I stand by my use of the word
"falsehood", and I invite jacob to correct me if I've misunderstood
what he wrote.

I have said what I said. If you choose not to listen, so be it.
 
K

Keith Thompson

[email protected] (Richard Harter) said:
On Sun, 13 Jan 2008 08:23:22 -0800, Keith Thompson

[...]
comp.std.c is for discussing the standard language.

Please stop repeating this falsehood. comp.std.c is for discussing
the language standard.

"Falsehood" is far too strong. "Infelicitous phrasing" would be
better, particularly since it is likely that English is not
Jacob's native tongue. He might have well meant that which you
more accurately phrased.
[...]

I don't believe so. I believe that jacob meant exactly what he wrote,
that comp.std.c is for discussion of "the standard language". He's
made similar statements in the past. I stand by my use of the word
"falsehood", and I invite jacob to correct me if I've misunderstood
what he wrote.

I have said what I said. If you choose not to listen, so be it.

I listened. I merely disagree.
 
C

CBFalconer

Richard said:
Keith Thompson said:
comp.std.c is for discussing the standard language.

Please stop repeating this falsehood. comp.std.c is for discussing
the language standard.

"Falsehood" is far too strong. "Infelicitous phrasing" would be
better, particularly since it is likely that English is not
Jacob's native tongue. He might have well meant that which you
more accurately phrased.

I disagree. Jacob has been informed of this many times, and yet he
persists in repeating the erroneous information.
 
C

CBFalconer

jacob said:
Yes it does

int fn(void) {
int a = 6;
}

int main(void) {
a = 78;
fn();
}

Before the call to fn() the value of "a" is 78.
During the call to fn it is 6.

After the call to fn, the value of a REVERTS to its previous value
The last value defined is the first that goes away.

Last in, first out (LIFO)

No. Revise your demo code slightly and make it legal:

int fn(int *p) {int a = 6; return (a == *p);}

int main(void) {int a = 78; fn(&a); return 0;};

and now both 'a's are visible in fn. There is no reverting done.
 
G

Guest

...
The local variable a does not have to be deallocated after the call to
fn. It merely ceases to be accessible by name, because it goes out of
scope. Its lifetime may be extended by the implementation for whatever
reason.

The implementation cannot extend the lifetime of an object as it is
defined in section "Storage durations of objects" (e. g. N1256, 6.2.4).
 
W

Wolfgang Draxinger

jacob said:
[nonsense about the standard]

You're making the same error again: You mistake the C standard as
an implementation guide.

The C standard doesn't define the term "stack" bacause it is
irrellevant for the standard itself. The standard just defines
what a C compiler must understand, and what, in the end, the
result of the written code shall _do_.

The standard does NOT, I repeat NOT, define a particular way, how
the details of an implementation shall be implemented. Of course
a stack is the natural way to implement recursive function
calls, as it's the natural way to store the return address, to
where to jump when a function is left.

However if the term "stack" would have been included into the
wording of the C standard, this would imply, also to define all
the dirty details of how to do things on the stack within the
standard. Unfortunately the way, how a stack works can largely
differ between architectured. It would also mean, that
inevitably the language shall provide means to manipulate the
stack. This would however imply, that a language, that defines a
stack would be limited to the common denominator of stack
implementations across architectures, which however doesn't
exist: Have one architecture, which has a stack, that works
orthogonally to other architectures, and the standard would be
no longer architecture independent.

Or in other terms: Just because the C language standard doesn't
define the term stack this doesn't mean, that the authors
neglegted the existence of a stack. It just means, that the
concept of a stack is irrellevant within the well defined terms
of a programming _language_ definition. It doesn't tell anything
about the _runtime implementation_.

If you're fluent in mathematics take it this way: A mathematic
theorem doesn't define algorithms. But theorems give you the
definitions of the "mathematic standard", algorithms are
implementations based on them.

Wolfgang Draxinger
 
J

James Kuyper

The implementation cannot extend the lifetime of an object as it is
defined in section "Storage durations of objects" (e. g. N1256, 6.2.4).

True, the lifetime is very precisely defined by that clause. However,
the length of time that memory is reserved for an object is only
constrained by that clause, it is not completely specified. That clause
says that the "The _lifetime_ of an object is the portion of program
execution during which storage is guaranteed to be reserved for it."
Storage for an object with automatic storage duration can continue to
reserved even after the guarantee no longer applies - the standard says
nothing to prohibit such an implementation.

As a matter of QoI, any reservations of large amounts of storage space
should be canceled reasonably promptly after the end of an object's
lifetime, or your program is going to unnecessarily run out of memory,
but even QoI doesn't impose strict LIFO ordering as an absolute
requirement, just an optimization that could be omitted if for some
peculiar reason (I've already suggested one possibility), it isn't an
optimization on some particular platform.

The standard does say something to prohibit this for objects with
dynamic storage duration, but only in reverse: deallocation is what
causes the lifetime to end, not vice versa. There's no such wording for
objects with automatic storage duration.
 
K

Keith Thompson

Wolfgang Draxinger said:
jacob said:
[nonsense about the standard]

You're making the same error again: You mistake the C standard as
an implementation guide.

The C standard doesn't define the term "stack" bacause it is
irrellevant for the standard itself. The standard just defines
what a C compiler must understand, and what, in the end, the
result of the written code shall _do_.

The standard does NOT, I repeat NOT, define a particular way, how
the details of an implementation shall be implemented. Of course
a stack is the natural way to implement recursive function
calls, as it's the natural way to store the return address, to
where to jump when a function is left.
[...]

And yet another poster throws around the word "stack" without
specifying what it means. One more time, there are two distinct
definitions of the word "stack" in common use: a "hardware stack",
consisting of a contiguous region of memory managed by a "stack
pointer", and a "stack" in the computer science sense of a last-in
first-out data structure, which could be implemented in any of a
number of ways.

The distinction between these two meaning has become the focus of this
thread. Using the word "stack" without qualification merely causes
further confusion.
 
C

CBFalconer

Chris said:
.... snip ...

Well that is an even tighter and artificial restriction on this
group with is computer languages :- C not Standard C or god
forbid only portable standard C

No, it isn't 'tighter'. The ISO standard is known. Arbitrary
extensions and deviations are unknown. When a program will run on
any system that meets that standard, it can be expected to run on
all such systems. It is simply a necessary characteristic for
portability.
 
K

Kaz Kylheku

In this group, there are a group of people that insists that "C doesn't
use a stack" or that "C doesn't imply a stack" because there *could* be
some obscure  implementations somewhere in extremely small circuits
where  there is no hardware stack.

As has been pointed out several times, the support of recursive
functions in C implies a logical stack.

In proper C terminology, this is called ``automatic storage''.

One of the "implementations"
this people presented, didn't implement recursion and can't be counted
as a complete C implementation.

The relevant part of the standard is (6.5.2.2.11):

<quote>
Recursive function calls shall be permitted, both directly and
indirectly through any chain of other functions
<end quote>

Even if several people have pointed out the fact that the language
implies a logical stack (recursion is part of the language), this
people continue to answer in stupid messages like

<quote>
Repeat after me: "There is no stack in the definition of the C
Language"
<end quote>

Automatic storage can be implemented using dynamic allocation and
deallocation, rather than a stack.

Automatic storage does form a logical stack, in the sense of the word
when it is generalized to refer to any kind of LIFO structure in
computer science.

This is not what people mean by ``stack'' in the context of the
machine representations of programs; the term ``stack'' refers to a
linear block of memory where allocation and deallocation is performed
by moving a pointer.
This fits in the general attitude of these people that like to
show their detail knowledge instead of realizing that the overwhelming
part of all C implementations use a hardware stack because the
overwhelming majority of processors has a hardware stack.

That is false. Many modern processors have no special hardware support
for a stack. Some general-purpose register is designated by convention
to serve as a stack pointer, whose value is maintained using ordinary
arithmetic instructions, and which is indirected upon using ordinary
addressing modes. The memory region for the stack is allocated using
ordinary means also.

Maybe by ``overwhelming majority'' you are referring to the installed
base of X86 compatible processors.
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top