The machine stack and the C language

R

robertwessel2

The stack is such a fundamental concept in computer architecture
however, that there is always some convention either dictated by
hardware or by the OS that will assign the stack management
to a special register.


I still think that's too strong a statement. Probably *today* it's
basically true for general computing devices, but not for embedded
systems. In the past it was also untrue for many "serious" systems.
Consider that old Cobol or Fortran had no need of anything stack like,
and that was the primary workload of many machines. And given the
monolithic nature of many programs from that era, calling conventions
were often fairly well ignored, especially within a program. IIOW,
even on S/360, you'd only see the standard calling linkage/save areas
used when you called a Fortran subprogram from a Cobol one, not when
you called a Cobol subroutine from inside the same Cobol program, and
even then, it was typically completely static in nature - IOW, the
save area that the Cobol program needed to supply to the Fortran
program was statically allocated in the Cobol programs load image.
 
R

robertwessel2

The power PC (the archetypal RISC)


Certainly PPC is *not* the archetypical RISC. Far from it, in fact.
PPC includes some very non-RISC-traditional elements. The ISA is also
rather a latecomer in the RISC arena.

has a dedicated register
for maintaining the stack. (register 2 if I remeber correctly)


No, it's actually a dedicated register in the branch unit (addressed
as SPR8) that hold the result of the last branch-and-link (subroutine
call). The branch unit also holds a dedicate count register used for
implementing "loop" like instructions. In both cases the application
has save/restore those as necessary (per the ABI) around subroutine
calls.

There is no hardware SP, the ABIs of most OS's do assign one of the
GPRs to SP duties, however.
 
K

Kaz Kylheku

MIPS family.

How many lies do I have to argue against?

http://dkrizanc.web.wesleyan.edu/courses/231/07/mips-spim.pdf
MIPS Assembly Language Programming
Page 49:
2. The callee must, as part of the function preamble:
(a) Create a stack frame, by subtracting the frame size from the stack
pointer ($sp).
Note that the minimum stack frame size in the MIPS software architecture
is 32 bytes, so even if you don't need all of this space, you should
still make your stack frames this large.
When the stack register is assigned by the OS ABI, that means it's not
a ``hardware'' stack.

No, a HARDWARE register is a software stack :)

WORD GAMES AND WORD GAMES. Regular do not know anything else

:)

At least it is fun!!
Only a software stack is needed for this.
Some RISC processors only save minimal information to be able to
return from a subroutine call. That information is the return address,
and it goes into a register. Returning from the subroutine is done by
branching to the address stored in that register. No stack is
involved.

The power PC (the archetypal RISC) has a dedicated register
for maintaining the stack. (register 2 if I remeber correctly)

WRONG AGAIN!




This simply requires a modified jump instruction which specifies a
register where the instruction pointer is stored. A regular indirect
jump is then used to perform the return.
From the CINT documentation:

``[E]very object created by CINT is a heap object so CINT does not
need the distinction between heap and stack based objects.''

This produces that CINT is not C...
<quote>
# Scope:
  + Function scope
    Variables can be declared in global or function scope.
    Unlike ANSI, local variable has function scope. If a local variable is
   once declared in a function, it will be alive until execution gets out
   from the function even if it is declared within sub-block. In ANSI C,
local
   variables have block scope.

        void func()
        {
          int i;
          for(i=0;i<10;i++) {
            int n;      /* block scope */
            printf("n=%d\\n",n++);
          }
          /* n is still alive here in cint, n should be already
          dead in ANSI C */
        }
<end quote>

That's the problem with not having a stack maybe.

Baloney. The above issue has nothing to do with stacks. It's simply a
parsing and name lookup issue. A conforming C compiler can let you
access n outside of the block where it is defined, provided that a
diagnostic is issued.

It's perfectly legitimate to treat an entire function scope as one
unit, such that all local variables are given unique locations within
the corresponding activation frame.

The simplest possible algorithm is to simply allocate local variables
in one big frame as the function body is parsed top to bottom.
Whenever a new declaration is parsed, regardless of the block scope in
which it appears, space is set aside and an entry is made under that
name in a symbol table. A name reference goes to the most recent
definition.

For conformance, all you need is to mark definitions out of scope so
that you can diagnose dangling name references.

C doesn't have true lexical scope (i.e. lexical closures) so issues of
instantiation of block scope locals don't matter.

If you could create a lexical closure within that for loop, then it
would matter, because on each iteration, your closure would want to
capture a different instance of n, yet each of these closures would
share the same instance of i.

(Needless to say, a C dialect supporting lexical closures wouldn't be
able to use a conventional flat stack for automatic storage).
.. ANSI C requires a stack.

Except for the problem that the document doesn't even use the word. Of
course ISO C requires automatic storage: a way to have fresh instances
of local variables, and a way to remember how to get back to the
suspended parent function.

We can't really call this a stack, because not all instances of it
require anything like it.

For instance a trivial leaf function might be compiled into code that
uses registers for representing all local variables (including
incoming parameters), and which returns by branching to an address
which also came in a register, and which passes back a return value in
a register. Such a function effectively has no stack frame (even
though other functions in the same program might have one).
In the abstract language, though, the function has automatic storage.

In comp.lang.c we emphasize the abstract language, because this is the
best way to serve the needs of newbies in their quest for
enlightenment. They are confused by thoughts like ``there is something
called a stack, and a function always has a stack frame''.
Other problems arise with variable argument functions.

Actually if you read it carefully, the issues are with foreign calls
to compiled variadic functions. For that to work, an architecture-
specific frame has to be generated. If you're calling something that
expects a stack, by golly, you have to set one up.

It doesn't appear to be an internal issue with CINT interpreted
funtions calling variadic CINT functions.

For the long
list of non standard features seehttp://root.cern.ch/viewcvs/trunk/doc/limitati.txt?root=cint

It's actually quite a short list, which took only a few minutes to
read.
Conclusion: This implementation is an embedded interpreter that
is not really standard. It shows how necessary a stack is for
a full implementation of C since many of the incompatibilities
arise from this fact.

Outlandish claim.

Except for the foreign calling considerations, none of the other
issues in that list appear to be even remotely connected to how CINT
represents automatic storage.
 
K

Kaz Kylheku

How many lies do I have to argue against?

http://dkrizanc.web.wesleyan.edu/courses/231/07/mips-spim.pdf
MIPS Assembly Language Programming
Page 49:
2. The callee must, as part of the function preamble:
(a) Create a stack frame, by subtracting the frame size from the stack
pointer ($sp).

Except that not every callee must do any of this. Only callees that
take external function calls in a way that conforms to the ABI.

Within a module, you can do away with these calling conventions. A
private function need do nothing at all with the $SP register.

$SP itself just an assembler mnemonic for a numbered register that
behaves in a certain way if you follow the standard MIPS ABI. The
architecture itself doesn't dictate such use.
Note that the minimum stack frame size in the MIPS software architecture

Right, the /software/ architecture.
 
K

Keith Thompson

jacob navia said:
Kaz Kylheku wrote: [...]
From the CINT documentation:
http://root.cern.ch/root/CintInterpreter.html
``[E]very object created by CINT is a heap object so CINT does not
need the distinction between heap and stack based objects.''

This produces that CINT is not C...
<quote>
# Scope:
+ Function scope
Variables can be declared in global or function scope.
Unlike ANSI, local variable has function scope. If a local variable is
once declared in a function, it will be alive until execution gets out
from the function even if it is declared within sub-block. In ANSI
C, local
variables have block scope.

void func()
{
int i;
for(i=0;i<10;i++) {
int n; /* block scope */
printf("n=%d\\n",n++);
}
/* n is still alive here in cint, n should be already
dead in ANSI C */
}
<end quote>

The sample code invokes undefined behavior by examining the value of n
when it hasn't been initialized, and really doesn't demonstrate
anything about whether the implementation gets scoping right. I
suspect (but I'm only guessing) that the author misunderstood the
distinction between scope and lifetime.

If n is still *visible* after the closing "}" of the for loop, then
that's definitely a point of non-conformance. The *scope* of n ends
at the closing brace of the for loop. But sample code doesn't
actually do that.

Probably CINT allocates all automatic objects for a function in one
chunk when the function is entered, and doesn't do allocation on
entering a block or deallocation on leaving a block (other than the
outermost block of a function definition). But that's a perfectly
valid strategy, and it doesn't conflict in any way with the standard.
Both the lifetime and the scope of n end at the closing "}" of the for
loop, but the actual physical deallocation doesn't have to occur until
the end of the function, or perhaps even later.
That's the problem with not having a stack maybe... ANSI C requires
a stack.

The example demonstrates nothing like that.
Other problems arise with variable argument functions. For the long
list of non standard features see
http://root.cern.ch/viewcvs/trunk/doc/limitati.txt?root=cint

According to that web page, variadic functions are supported, but with
some limitations, mostly having to do with interpreted vs. compiled
functions. Some of the problems are caused by the need to interface
to various underlying platforms. I see nothing about problems being
caused by the lack of a contiguous stack.
Conclusion: This implementation is an embedded interpreter that
is not really standard.

Agreed, but I see no fundamental reason why it couldn't be made to
conform fully to the standard (except that the authors probably don't
consider it to be worth the effort).
It shows how necessary a stack is for
a full implementation of C since many of the incompatibilities
arise from this fact.

Not really.

[...]
 
W

Wolfgang Draxinger

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

Still, the C language standard works well without defining any of
these. When I'm using the word "stack" in (most) of my posting,
then it may refer to anything, even to pink bunnies in a closet,
which the computer uses for data management ;-) - the
term "stack" is not defined by the C language standard.

It's like if I'm saying "vector", and everybody thinks of
n-tuples of numbers. But anything can be a vector, as long it's
defined in term of a vector space.

And in this case, anything can be a "stack", as long it fullfills
a certain set of operations.
The distinction between these two meaning has become the focus
of this thread. Using the word "stack" without qualification
merely causes further confusion.

Such a distinction becomes important, when you implement a
runtime environment and a compiler. As long you only talk about
the language standard, "stack" is an abstract, and in case of
the C language redundant term, outside the scope of the language
definition.

Wolfgang Draxinger
 
P

Philip Potter

jacob said:
The power PC (the archetypal RISC) has a dedicated register
for maintaining the stack. (register 2 if I remeber correctly)

WRONG AGAIN!

Using all-caps does not make you more correct.

I would agree with both points of view in this particular case. If we
look at the ARM (the only vaguely RISClike I'm familiar with), there is
a hardware stack (by convention) but it is not generally used for the
current function. The current function has access to r0-r3, and the
return address is in r14. If it needs more, it can save r4-10 to the
stack and use those. But the use of the stack is not guaranteed. So for
example:

int f(int n) {
int k = n + 1;
return k;
}

would be implemented as the following nonoptimized pseudoassembly:

ADD r1, r0, 1 ; n passed as r0, k stored in r1
MOV r0, r1 ; move k to r0, where the return value is expected
JMP r14

And it would be called (roughly) like so:

<save r0-r3 and r14 to stack, if necessary>
MOV r14, pc ; save program counter as return address
JMP f

So you are correct that a stack exists, but Kaz is correct that it is
not used at all by f(), and it is not necessary to use it in order to
call f - if registers need to be saved, they could be saved to static or
dynamic memory instead.

Lets assume for a moment that you are correct and that every C
implementation uses a hardware stack. What does that gain you? You still
can't say anything like:
"Local variables are stored on the stack"
"The return address is stored in the current stack frame"
"Calling a function changes the stack"
because f() does not satisfy any of these and yet there exist conforming
C implementations on ARM. On top of that, because there are so many
different ways a hardware stack can be organised, it really doesn't gain
you anything.

I have some sympathy for your point of view - almost every C
implementation probably does use a hardware stack, even though the
Standard doesn't require it - but I don't feel that such knowledge is
useful, so why assert it?
 
M

Malcolm McLean

Jacob navia said:
The documentation specifies also that the function prolog allocates
a stack frame of at least 120 bytes, and that the
function epilogue deallocates that storage.

Since R13 is dedicated to this... you can take your own conclusions!
The MIPS idea was that all registers would be idential. Thus you could
generate op codes in the form

5 bits 5 bits 5bits bit for luck 16 bits
register A | register B | register C | |operation

(The details might be wrong, I'm working from memory here)
This simplified chip design. It also mean that you didn't have special
instructions like push / pop. Obviously you still had a program counter
register - the idea could be pushed only so far.
However you then have a set of conventions because assembly with register
numbers isn't terribly readable. One of these was that a register was
designated as the "stack pointer". Anohterh was that certain registers were
temporary and could be corrupted by leaf routines, whilst others had to be
preserved.
So you could write a tight little leaf routine that corrupted only the
temporary registers, and didn't need anything else except the jump back,
which was also in aregister designated by convention.
 
J

jacob navia

Philip said:
I have some sympathy for your point of view - almost every C
implementation probably does use a hardware stack, even though the
Standard doesn't require it - but I don't feel that such knowledge is
useful, so why assert it?

Because the original question was about stack overflow.
The "regulars" answer is

There is no stack in C. (The first answer from Mr Lew Pitcher.

I just said that this is not true, then a whole series of
wrong posts ensued, some of them trying to prove that
there is a "possible" C implementation that doesn't
use the stack, then they brought the IBM mainframes
argument (wrong) and then the Power PC argument (wrong too).

The only implementation that they could bring was a weird
C interpreter embedded in some CERN software that, as stated
in their own docs, is not standard.

I wat to bring an END to this deliberate leading astray
of people that ask questions with stupid answers like
"C doesn't use a stack".
 
P

Philip Potter

jacob said:
Because the original question was about stack overflow.
The "regulars" answer is

There is no stack in C. (The first answer from Mr Lew Pitcher.

I just said that this is not true, then a whole series of
wrong posts ensued, some of them trying to prove that
there is a "possible" C implementation that doesn't
use the stack, then they brought the IBM mainframes
argument (wrong) and then the Power PC argument (wrong too).

The only implementation that they could bring was a weird
C interpreter embedded in some CERN software that, as stated
in their own docs, is not standard.

There are two issues here:

1) Does C require a stack? Does C require a hardware stack?

[This is the question Lew focused on]

This question is interesting because it relies only on the text of the
Standard, and tells something about all possible C implementations. The
Standard does not define the term "stack", as has been said, but we can
certainly make inferences from the text of the Standard. The Standard's
requirements on the lifetime of variables are very loose, and if the
implementation wishes, it can make every variable exist from the start
to the end of the program. Even the case of multiple instances of local
variables in a recursive function can be catered for when we realise
that C doesn't guarantee infinite recursion depth.

I can also imagine a C implementation which provides closures as an
extension and as a result has a "spaghetti stack" which is certainly not
a true stack. I believe this shows that C does not require a stack.

2) Do all conceivable reasonable C implementations use a stack?

[This is the question jacob has been focusing on]

This question is interesting because answering this question allows us
to make slightly more assumptions than the answer to the above question
does. (One similar question is "Do all conceivable reasonable C
implementations have the English alphabet in increasing numerical
order?" to which many people take the answer to be "yes" even though the
Standard does not require it.)

Unfortunately I feel this question is too general to answer a definite
"yes". Even if all reasonable C implementations today use a stack,
there's no reason one couldn't come along tomorrow which someone
(perhaps me!) may wish to port my code to.

A third issue is:

3) Even if all conceivable reasonable implementations use a stack, does
that gain me anything? (In other words, is it even worth bothering to
answer question 2?)

And for me the answer is definitely "no".
I wat to bring an END to this deliberate leading astray
of people that ask questions with stupid answers like
"C doesn't use a stack".

The word is /require/, not /use/. And the C Standard does not require a
stack, though most implementations use one.
 
C

Chris Dollin

Philip said:
Using all-caps does not make you more correct.

I would agree with both points of view in this particular case. If we
look at the ARM (the only vaguely RISClike I'm familiar with), there is
a hardware stack (by convention) but it is not generally used for the
current function.

There's no "hardware stack", except in the sense that /any/ of
the registers (except PC) can be used as a stack pointer, and
/by convention/ one of them is used to point to the store used
to hold stackly things.

That register is "the" stack pointer because it's the one
selected by convention and so it makes sense to use it.
When it overflows, at least one ARM C compiler then grabs
a new chunk out of the heap: so automatic store is a bunch
of linked chunks each locally managed "by hardware" but
globally managed "by software", and there isn't just the
one contiguous region of store for "the stack" even within
a single application's user address space.
 
J

jacob navia

Chris said:
There's no "hardware stack", except in the sense that /any/ of
the registers (except PC) can be used as a stack pointer, and
/by convention/ one of them is used to point to the store used
to hold stackly things.

That register is "the" stack pointer because it's the one
selected by convention and so it makes sense to use it.
When it overflows, at least one ARM C compiler then grabs
a new chunk out of the heap: so automatic store is a bunch
of linked chunks each locally managed "by hardware" but
globally managed "by software", and there isn't just the
one contiguous region of store for "the stack" even within
a single application's user address space.

Interesting. The same holds for windows/linux and actually any OS using
virtual addresses!
 
K

Kenneth Brody

James Kuyper wrote:
[...]
True, but should such code be on-topic here? The US declaration of
independence is conforming C code, because a conforming implementation
of C can, as an extension, accept it after issuing at least one
diagnostic message. I believe that a conforming implementation of C
which accepts all code which has no #error directives that survive
conditional compilation, always producing at least one diagnostic
message, has in fact been created. Should that fact be used as an excuse
for discussing the US revolutionary war in this newsgroup? Where do you
draw the line?

But, the Declaration of Independence dereferences the value "Creator"
without first initializing it, thereby invoking UB. It also makes
reference to the incomplete struct "unalienableRights"[1].


[1] Or would that be an enum?

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
F

Flash Gordon

Philip Potter wrote, On 15/01/08 11:55:
So if all implementations use a stack do you therefore accept that all
implementations are relevant to a thread about stack exhaustion? This is
a serious question because people have pointed out implementations which
have something that you would describe as a stack (based on the above)
but where the stack (if it is one) is not stored linearly.
There are two issues here:

1) Does C require a stack? Does C require a hardware stack?

[This is the question Lew focused on]

This question is interesting because it relies only on the text of the

2) Do all conceivable reasonable C implementations use a stack?

[This is the question jacob has been focusing on]

Unfortunately I feel this question is too general to answer a definite
"yes". Even if all reasonable C implementations today use a stack,
there's no reason one couldn't come along tomorrow which someone
(perhaps me!) may wish to port my code to.

A third issue is:

3) Even if all conceivable reasonable implementations use a stack, does
that gain me anything? (In other words, is it even worth bothering to
answer question 2?)

And for me the answer is definitely "no".

I will introduced a few more points...

My favourite, the TMS320C2xx series of processors *does* have a HW stack
although there is *no* stack pointer for it. Return addresses are pushed
on to it by the processor on a call, and popped off on a return. There
are also instructions to push data on to the stack and pop it off.
However, that stack is *not* used by the C implementation to implement
the "C system stack" (Texas Instrument's term) because it is only deep.
So for this implementation referring to "the stack" is *very*
misleading, because "the stack" is the HW stack that never overflows
because the C compiler ensures that return addresses are popped off it
before it has a chance and automatic variables are never stored on it.

Also, what about an implementation that uses two stacks, one for return
addresses and a separate stack for automatic variables? This could fully
conform and would have major advantages, such as preventing buffer
overflows from overwriting the return address. On such an implementation
what would you mean by "the stack"?
The word is /require/, not /use/. And the C Standard does not require a
stack, though most implementations use one.

True, although it is not always "the HW stack" when the processor has one.
 
P

Paul Hsieh

That is false. Many modern processors have no special hardware support
for a stack.

What the hell are you guys arguing about? "Hardware support for a
stack" need not be anything more than an auto-incrementing/
decrementing memory register. And if you don't have such a thing, but
can increment/decrement the memory register in parallel in some free
pipeline, what the hell is the real difference? When talking about
general stacks, even the most hardwarish of hardware stacks is not
significantly distinguished form just rolling your own from scratch.
[...] 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.

In the context of C, the only relevance is how this may or may not be
interleaved with the link register. I.e, does the auto-data mix with
return addresses. In non-failing code there shouldn't be a
difference, and there is a rare application that can take advantage of
this in any relevant way (one where you are rolling your own tasking,
perhaps.)
Maybe by ``overwhelming majority'' you are referring to the installed
base of X86 compatible processors.

Better add UltraSparc, Itanium and PDP/11 to the that list.
 
P

Paul Hsieh

Then you're basically arguing about the semantics of the word
``stack''.

When most confused newbies use the term ``stack'', they do not have
this generalized idea in mind.

They don't? Since when do newbies know about hardware machine
implementations, but not basic data structures?
That is false. A stack pointer is a device that is incremented and
decremented in order to push and pop individual words.

The reference to the top frame of a logical stack isn't such a stack
pointer. It's never called a ``stack pointer''.

Its not? What is it called then? (I would call it a "stack pointer",
or "logical stack pointer", since that's easier to say than "top of
stack reference".)
 
C

Chris Dollin

jacob said:
Chris Dollin wrote:

Interesting. The same holds for windows/linux and actually any OS using
virtual addresses!

That only matters if you think that the /physical/ memory address
has anything to do with the point, and in spite of the fact that
an OS using virtual addresses would be free to allocate /contiguous/
physical memory to a single process -- as, if I recall correctly,
some of them did.

Whether something is a "stack" or not is a point of view, and like
all points of view matters mainly in the light of what difference
it makes to what is done. If one thinks that X is likely to think
a "stack" is a contiguous sequence of memory locations with a
distinguished "top" pointer, then one can correctly -- and perhaps
wisely -- tell them that C doesn't /require/ a stack for its
automatic variables. If you think X has a more abstract, behavioural
view of what a stack "is", then you can say, yes, C's requirements
for automatic variables are exactly those of a stack, although
some C programs don't require the run-time pushing and popping
that would imply.

I think it's Keith that's been insisting we should distinguish
stack (the ADT) with stack (contiguous memory + pointer); to
which I say, +1, brother.

/I/ think that people who come here and ask about C and "the
stack" are, usually, thinking of the contiguous-memory thing
with An Important Stack Pointer, and so deserve -- gentle --
illumination and perhaps asking /why/ they're asking, because
it really makes a difference.
 
R

robertwessel2

Also, what about an implementation that uses two stacks, one for return
addresses and a separate stack for automatic variables? This could fully
conform and would have major advantages, such as preventing buffer
overflows from overwriting the return address. On such an implementation
what would you mean by "the stack"?


FWIW, all C implementations for IPF that I'm aware of do just that
(it's possible to not use the RSE in IPF, I just don't know of anyone
who's done that). Although in that case it's all of the integer
registers which are saved on the separate register stack (and the
return address is saved there since return addresses are stored in
registers by the subroutine calls).

SPARC is the same way, although the details of the register windows
stack are a bit different.

In both cases registers are lazily saved to the (register) stack only
when the hardware runs out of registers.

As a general comment, a lot of this discussion revolves around the
overloading of the term stack. Let's all talk about the definition of
a heap next, OK?

The semantics of subroutine calls in C clearly require automatics to
behave in a LIFO manner, and it's perfectly reasonable to call the
current thread of activation records, no matter the implementation, a
stack. If the compiler can determine that no recursion is going to
occur, it could plausible allocate all of the storage for automatics,
parameters, register save areas, and return address save areas
statically, and leave no code that "manages" any sort of stack
structure, or manages a "stack" only for the parts of a program that
can be used recursively. But even then the logical structure of the
currently active activation records is that of a LIFO or stack.

A number of older languages made recursability (is that a word?) an
optional attribution of a function. PL/1, for example, required that
you tag any recursive functions with "recursive" - and storage for all
other functions was typically allocated in a purely static fashion.

Which brings up the real question, is that knowledge useful in any
way? Probably only for reasons that exist outside of the C standard -
interfacing to other languages, debugging, etc.
 
J

jameskuyper

Paul said:
They don't? Since when do newbies know about hardware machine
implementations, but not basic data structures?

Most of the newbies I've known were much better informed about machine
architecture (for one particular kind of machine) than about abstract
data structures. YMMV.
 
J

jameskuyper

Kenneth said:
James Kuyper wrote:
[...]
True, but should such code be on-topic here? The US declaration of
independence is conforming C code, because a conforming implementation
of C can, as an extension, accept it after issuing at least one
diagnostic message. I believe that a conforming implementation of C
which accepts all code which has no #error directives that survive
conditional compilation, always producing at least one diagnostic
message, has in fact been created. Should that fact be used as an excuse
for discussing the US revolutionary war in this newsgroup? Where do you
draw the line?

But, the Declaration of Independence dereferences the value "Creator"
without first initializing it, thereby invoking UB. It also makes
reference to the incomplete struct "unalienableRights"[1].

Code containing undefined behavior cannot qualify as "strictly
conforming", but that's no barrier to qualifying as "conforming".
Syntax errors and constraint violations are no problem either. The
only thing that is problematic is a correctly formatted #error
directive, and the Declaration contains not a single one of those.
This renders the term "conforming code" almost completely pointless,
but that was recognised when the current definition of that term was
first created. It was a political compromise whose only value lies in
the fact that it was a political necessity in order to get the
standard to be approved.

Most C extensions are implemented by having the implementation define
specific behavior for code which the C standard specifies as having no
defined behavior. Invoking, for instance, a spell checker rather than
a compiler in such cases would be a perfectly legitimate extension for
a conforming implementation of C.
 

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

Latest Threads

Top