Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Re: A portable stack in C

Reply
Thread Tools

Re: A portable stack in C

 
 
jacob navia
Guest
Posts: n/a
 
      11-15-2011
Le 15/11/11 13:39, Ben Bacarisse a écrit :
> jacob navia<> writes:
>
>> Le 15/11/11 12:45, Ben Bacarisse a écrit :
>>> jacob navia<> writes:
>>> <snip>
>>>> Conceptually ANY pointer is an index in a big array called "memory"
>>>> (or RAM if you like).
>>>
>>> C pointers don't fit this concept. They have a type which contributes
>>> to the result of operations like *p, sizeof *p and p + 1.
>>>

>>
>> The *p operation is just
>> 1) Read the value of the pointer
>> 2) Index the memory
>> 3) read that position.
>>
>> This is just memory[p] (assuming non segmented memory)
>>
>> sizeof(*p)<==> sizeof (memory[p]) where you would have
>> conceptually "memory" as an aray of *p.

>
> But you want to suggest that a pointer in an index into one big array
> called memory. What type does this one big memory array have?
>
> I know what you mean. The type of the big array will keep changing
> depending on the context, but that just shows why you can't say that a
> pointer "is an index in a big array called 'memory'" -- pointers don't
> fit the concept of an index into an array, because an array has one type
> for all it's elements.
>


Yes, but you can see a long double as an array of 10 bytes, or 5 shorts,
or many other representations. That is the "pointed at" type, not
the value of the pointer itself. The value of the pointer is always the
value of an index into a byte array starting at virtual memory address
zero and going on up to the top of the virtual memory space.

>> The "p + 1" operation is the same:
>> memory[p+1] where "memory" is an array of objects of type *p.

>
> This last one is wrong, even assuming a magic array whose type can be
> whatever is need at the time.
>


That is what I am saying. Suppose

long double *pLongDouble = &var;

If you look at the VALUE of that pointer you will read (say)
0x7DF4500

What is that number?

Is the NUMBER of memory locations starting at zero that you should
skip to arrive at that value. It is NOT the number of doubles,
or long doubles or the type of whatever that pointer is being used
for.

All pointers use the same UNIT: memory locations. Of course they
dereference different types, etc. But basically they are all the same.

They are an index into the "memory" array. (Assuming non-segmented
memory)

>>> C's function pointers don't fit well into the "memory index" model
>>> either because you can do any array-like operations with them.

>>
>> Function pointers can be emulated as a conceptual table of
>> machine instructions where p points to the start of the
>> sequence of instructions of a function.

>
> So you think it's helpful to consider a function pointer to be an index
> into array, but one you can't do any array operations on?


Look:

double fn(int a, int b)
{
return a+b;
}

unsigned char code_for_function_fn[] = {
0x55,0x89,0xe5,0x57,0x8b,0x7d,0x08,
0x0",0x7d,0x0c,0x57,0xdb,0x04,0x24,
0x83,0xc4,0x04,0x5f,0x5d,0xc3};


Both representations of the function "fn" are identical. The
proof is that if you disassemble "fn" you will obtain:

[0000000] 55 push %ebp
[0000001] 89e5 mov %esp,%ebp
[0000003] 57 push %edi
[0000004] 8b7d08 mov 0x8(%ebp),%edi
[0000007] 037d0c add 0xc(%ebp),%edi
[0000010] 57 push %edi
[0000011] db0424 fildl (%esp,1)
[0000014] 83c404 add $0x4,%esp
[0000017] 5f pop %edi
[0000018] 5d pop %ebp
[0000019] c3 ret


If you cast the function pointer of "fn" into a char pointer, you can
even do a memcmp to verify that (obviously the actual contents could
be different in a different version of lcc-win or with another compiler
but that is just trivial).

All functions are just an array of instructions (well, a special array
since instructions need not to be of the same length)

> How does that
> help?


It helps to avoid being caught by software and to realize that actually
it is the hardware that does everything.

It helps because it helps you realize how the machine actually WORKS...

What type is this new array or is it the same as the other array?

There is only ONE type for pointers: Memory locations (consecutive
in a linear architecture, segmented in a "Harvard" architecture)

> To my mind, this is am attempted simplification that complicates matters.


Sure, but this is how machines actually WORK.

>
>> In general, what I want to emphasize is the equivalence between array
>> indices and pointers.

>
> In general, I don't want to do that.
>


You will agree that *p is the same as p[0] isn't it? For ALL p?

Why is that?

Anyway, I understand why you do not want to do that in general. Because
you are concerned with software and with high level languages (that
exist only in software). And that is a very legitimate concern.

I am at the interface of software and hardware. My job is to translate
software into codes that the hardware understands, so I am more
interested in knowing how a machine actually works beyond software
abstractions.

 
Reply With Quote
 
 
 
 
Ben Bacarisse
Guest
Posts: n/a
 
      11-15-2011
jacob navia <> writes:

> Le 15/11/11 13:39, Ben Bacarisse a écrit :
>> jacob navia<> writes:
>>
>>> Le 15/11/11 12:45, Ben Bacarisse a écrit :
>>>> jacob navia<> writes:
>>>> <snip>
>>>>> Conceptually ANY pointer is an index in a big array called "memory"
>>>>> (or RAM if you like).
>>>>
>>>> C pointers don't fit this concept. They have a type which contributes
>>>> to the result of operations like *p, sizeof *p and p + 1.
>>>>
>>>
>>> The *p operation is just
>>> 1) Read the value of the pointer
>>> 2) Index the memory
>>> 3) read that position.
>>>
>>> This is just memory[p] (assuming non segmented memory)
>>>
>>> sizeof(*p)<==> sizeof (memory[p]) where you would have
>>> conceptually "memory" as an aray of *p.

>>
>> But you want to suggest that a pointer in an index into one big array
>> called memory. What type does this one big memory array have?
>>
>> I know what you mean. The type of the big array will keep changing
>> depending on the context, but that just shows why you can't say that a
>> pointer "is an index in a big array called 'memory'" -- pointers don't
>> fit the concept of an index into an array, because an array has one type
>> for all it's elements.
>>

>
> Yes, but you can see a long double as an array of 10 bytes, or 5 shorts,
> or many other representations. That is the "pointed at" type, not
> the value of the pointer itself. The value of the pointer is always
> the value of an index into a byte array starting at virtual memory
> address
> zero and going on up to the top of the virtual memory space.
>
>>> The "p + 1" operation is the same:
>>> memory[p+1] where "memory" is an array of objects of type *p.

>>
>> This last one is wrong, even assuming a magic array whose type can be
>> whatever is need at the time.
>>

>
> That is what I am saying.


No, look at what you wrote. Even in your world of indexes, p+1 is not
memory[p+1] as you wrote. You might have meant &memory[p+1] but I don't
want to guess (and you'd have to say what & means in this big array
index world).

> Suppose
>
> long double *pLongDouble = &var;
>
> If you look at the VALUE of that pointer you will read (say)
> 0x7DF4500


Do you know that this is ambiguous? A value (in C) is a pile of bits
/interpreted as a value of some type/. Do you believe that the value of
that pointer is always -- on all hardware -- independent of the type
used to interpret the bits?

In a debugger, the bits are often interpreted as an unsigned int, so,
for the time being, I'll assume that this is what you mean by the value
of a pointer.

> What is that number?
>
> Is the NUMBER of memory locations starting at zero that you should
> skip to arrive at that value. It is NOT the number of doubles,
> or long doubles or the type of whatever that pointer is being used
> for.
>
> All pointers use the same UNIT: memory locations. Of course they
> dereference different types, etc. But basically they are all the same.


I don't know what this means. What is this UNIT? If you mean that all
machine addresses use the same measure to determine where they point,
this is not true. It can depend on the type. C specifically allows for
different kinds of machine address that may use different units. If
you've even wandered why C's rules of pointer conversion are the way
they are, it is largely because of this.

Note I switched to "machine address" rather than "pointer". You, of all
people, know that C pointers don't use the same measure, but I think you
think that once you've moved into the realm of machine addresses, it
becomes true. It does not (though I grant you it does on all the
hardware you've probably used).

> They are an index into the "memory" array. (Assuming non-segmented
> memory)


Not all pointers. I've used a machine where function pointers "looked
like" small numbers. The address of your first function was 0. The
second was 1, the third 2, and so on. In what useful way are these
indexes into some giant array called "memory"?

>>>> C's function pointers don't fit well into the "memory index" model
>>>> either because you can do any array-like operations with them.
>>>
>>> Function pointers can be emulated as a conceptual table of
>>> machine instructions where p points to the start of the
>>> sequence of instructions of a function.

>>
>> So you think it's helpful to consider a function pointer to be an index
>> into array, but one you can't do any array operations on?

>
> Look:
>
> double fn(int a, int b)
> {
> return a+b;
> }
>
> unsigned char code_for_function_fn[] = {
> 0x55,0x89,0xe5,0x57,0x8b,0x7d,0x08,
> 0x0",0x7d,0x0c,0x57,0xdb,0x04,0x24,
> 0x83,0xc4,0x04,0x5f,0x5d,0xc3};
>
> Both representations of the function "fn" are identical.


I think you misunderstand the meaning of identical. One is portable
across machines and the other is not. One can be called without
undefined behaviour the other can not, and so on.

> The
> proof is that if you disassemble "fn" you will obtain:
>
> [0000000] 55 push %ebp
> [0000001] 89e5 mov %esp,%ebp
> [0000003] 57 push %edi
> [0000004] 8b7d08 mov 0x8(%ebp),%edi
> [0000007] 037d0c add 0xc(%ebp),%edi
> [0000010] 57 push %edi
> [0000011] db0424 fildl (%esp,1)
> [0000014] 83c404 add $0x4,%esp
> [0000017] 5f pop %edi
> [0000018] 5d pop %ebp
> [0000019] c3 ret


Honestly, do you really think I don't how it's usually done? My point
is that is that it's not /always/ done this way, and that makes the
"pointer == machine address == index into a big array" model misleading.

> If you cast the function pointer of "fn" into a char pointer,


....you get undefined behaviour.

> you can
> even do a memcmp to verify that (obviously the actual contents could
> be different in a different version of lcc-win or with another compiler
> but that is just trivial).


Not without UB.

> All functions are just an array of instructions (well, a special array
> since instructions need not to be of the same length)


but the instructions need not be readable as data, and the pointer might
not have a representation that relates in any way to a data pointer.

>> How does that
>> help?

>
> It helps to avoid being caught by software and to realize that actually
> it is the hardware that does everything.
>
> It helps because it helps you realize how the machine actually
> WORKS...


You mean how some machines work. I think machine addressing is best
explained by describing it directly. C deliberately abstracts out all
the possible differences between (many) machines so it does not work
well as a way to explain the possibilities.

> What type is this new array or is it the same as the other array?


(this is a miss-quote: I said it, not you).

> There is only ONE type for pointers: Memory locations (consecutive
> in a linear architecture, segmented in a "Harvard" architecture)


So on the machine I described above (function pointers being 0, 1, 2
etc), what is the type of the array into which these are indexes?

>> To my mind, this is am attempted simplification that complicates matters.

>
> Sure, but this is how machines actually WORK.


It's how many machines work, but you went further. You were using this
idea to explain C pointers, and they don't work like this.

>>> In general, what I want to emphasize is the equivalence between array
>>> indices and pointers.

>>
>> In general, I don't want to do that.
>>

>
> You will agree that *p is the same as p[0] isn't it? For ALL p?


Kinda. It's true in the sense that you get the same result when the
result is defined, and that both are undefined if either one is
undefined. Are two "undefined behaviours" always "the same"? A thread
about that could run and run.

> Why is that?


Because the language defines p[0] like that (when both are defined).

> Anyway, I understand why you do not want to do that in general. Because
> you are concerned with software and with high level languages (that
> exist only in software). And that is a very legitimate concern.


No. I used to teach machine architecture as well, and C hides far
too much of what is going on to be useful. It's useful to explain how C
can be implemented on, say, a word addressed machine, but you can't
illustrate the various address type in C (well, not easily) because
C, rightly, hides all that.

> I am at the interface of software and hardware. My job is to translate
> software into codes that the hardware understands, so I am more
> interested in knowing how a machine actually works beyond software
> abstractions.


Sure, but that does not mean the software abstractions don't exist. If
this had started as a post about "what the hardware does" it would
merely be a limited perspective, but you started by saying that C
pointers are array indexes, and that is sufficiently wrong as to be
confusing to people learning C.

--
Ben.
 
Reply With Quote
 
 
 
 
Thomas David Rivers
Guest
Posts: n/a
 
      11-15-2011
jacob navia wrote:

>
>
>>> is it possible using that stack for call of function as
>>> f(a, b)<=> push32 b|push32 a| call32 f

>>
>>
>> Not on my system which doesn't use a stack or have a push instruction.
>>

>
> Ahh OK;
>
>> You shouldn't believe everything you read on Usenet.
>>

>
> You are right. I don't believe you that there isn't (by convention)
> a register pointing to the stack, and that
>
> sub stack,8
> move value,[stack]
>
> doesn't work.
>
> Which system are you using?



I believe ( if I have my Barry's right) that Barry may be working on an IBM
mainframe.

We happen to make C/C++ compilers for the mainframe...

There is no hardware stack (well - technically there is, but it's not very
convenient and isn't used much.... it's a "newer" addition to the hardware
and is a little cumbersome to use and pretty much only stacks the
registers.)

On the IBM mainframe, programs typically do reserve a register to
be the "stack pointer" - they call it the "dynamic save area" (DSA).
Most of the time, this is R13, but that is only by convention.. and many
programmers choose alternatives.

There is no "call" instruction - there is a BALR (Branch and Link Register).
BALR names two registers - the first one receives the address of the
instruction following the BALR, the 2nd one has the branch target.

Thus - to effect a "call", you use BALR to jump to the routine. The
routine
is then obliged to allocate some space and point R13 to it. It can then
save the remaining registers along with the return address (loaded into R14
by the BALR instruction.) You also want to save the previous R13 before
pointing at the new space.

(There is a newer variant of the BALR, named BASR - to handle some
addressing mode situations... newer being, only 20-30 yrs old... BALR
was part of the original specification.)

The "allocation of space" can be as simple as pointing R13 at some static
space in the program; or as complicated as invoking operating system
services to dynamically allocate space, or having the runtime system
manage this space by allocating blocks and giving up chunks of the blocks
as needed.

Then, on return, you load R13 from the saved location (to restore its
previous
value) as well as the other registers... which includes the initial
value of R14.
And, you branch back to the location specified in R14. (You may also have
to de-allocate the space allocated at the start.)

The act of
1) Save R13
2) Point R13 at some new space
...
n) Restore R13 to old value

clearly is a "stack" in the C sense. But, it is interesting to note that
this stack is not contiguous. One stack location doesn't necessarily
have any bearing on another, etc...

This is by convention only - and many mainframe programming
environments decide to abandon that convention... making it rather
frustrating to mix-and-match language implementations or runtime
environments.

So - you are partially right - there is by convention a "stack pointer",
but you can't just subtract a value from it any expect it to work
in any meaningful fashion.

There are aveats here - the newer, post 1990 hardware implemented guard
pages and copy-on-write in the virtual memory addressing system,
so there are new linkage conventions - called XPLINK, that get
close to the subtrace-a-value-from-the-stack-pointer paradigm. But, if the
value you want to subtract is larger than the hardware page size,
then you still have to "punt out" to an out-of-line routine to allocate
stack space and move the guard page...

Also - this all pertains to MVS and z/OS. The z/Linux linkage conventions
follow the z/Linux ELF ABI, where R15 is the stack pointer... and where
things work as you might expect.


- Dave Rivers -

--
Work: (919) 676-0847
Get your mainframe programming tools at http://www.dignus.com
 
Reply With Quote
 
BartC
Guest
Posts: n/a
 
      11-15-2011

"jacob navia" <> wrote in message
news:j9tk3s$7bl$...
> Le 15/11/11 12:45, Ben Bacarisse a écrit :
>> jacob navia<> writes:
>> <snip>
>>> Conceptually ANY pointer is an index in a big array called "memory"
>>> (or RAM if you like).

>>
>> C pointers don't fit this concept. They have a type which contributes
>> to the result of operations like *p, sizeof *p and p + 1.
>>

>
> The *p operation is just
> 1) Read the value of the pointer
> 2) Index the memory
> 3) read that position.
>
> This is just memory[p] (assuming non segmented memory)



Yes, you can throw away everything in C, and just have a gigantic static
array of bytes 'memory'.

But superimposing ordinary variables, arrays, and structs on top of that is
fiddly.

However, even supposing that was done, that's not sufficient, because each
function invocation will have it's own local byte-array for local variables.
Now, this array can presumably also be located a space somewhere in the
global array, but then, so what?

I'm not sure where you're going with this. You know have control over the
size and range of pointers, by using indexes (although if memory[] is the
same size as the machine's memory, the width of your index will be the same
as the width of your pointers), but you've thrown away half of C too, and
programming becomes next to impossible.

Maybe this is how it's all done at a low-level, but I don't think it's what
that poster had in mind.

> In general, what I want to emphasize is the equivalence between array
> indices and pointers.


Array accesses usually involve two elements: the array, and the index. And
the array itself will be a pointer (that is, if you forget the nonsense
about a giant byte-array).

While pointer accesses usually involve one element, the pointer value, with
an optional offset.

And if you have this:

int A;

and want to pass a pointer to A to some function, how exactly would you turn
that into an index, without rewriting your entire application in a
stripped-down, low-level version of C?

--
Bartc

 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      11-15-2011
Ben Bacarisse <> writes:
> jacob navia <> writes:
> <snip>
>> Conceptually ANY pointer is an index in a big array called "memory"
>> (or RAM if you like).

>
> C pointers don't fit this concept. They have a type which contributes
> to the result of operations like *p, sizeof *p and p + 1.
>
> C's function pointers don't fit well into the "memory index" model
> either because you can do any array-like operations with them.
>
> <snip>


And the C memory model does *not* treat all of memory as a single array.

--
Keith Thompson (The_Other_Keith) kst- <http://www.ghoti.net/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      11-15-2011
Ben Bacarisse <> writes:
> jacob navia <> writes:

[...]
>> You will agree that *p is the same as p[0] isn't it? For ALL p?

>
> Kinda. It's true in the sense that you get the same result when the
> result is defined, and that both are undefined if either one is
> undefined. Are two "undefined behaviours" always "the same"? A thread
> about that could run and run.


And it's not true of p is a function pointer.

--
Keith Thompson (The_Other_Keith) kst- <http://www.ghoti.net/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      11-15-2011
Keith Thompson <kst-> writes:

> Ben Bacarisse <> writes:
>> jacob navia <> writes:

> [...]
>>> You will agree that *p is the same as p[0] isn't it? For ALL p?

>>
>> Kinda. It's true in the sense that you get the same result when the
>> result is defined, and that both are undefined if either one is
>> undefined. Are two "undefined behaviours" always "the same"? A thread
>> about that could run and run.

>
> And it's not true of p is a function pointer.


Surely both *p and p[0] are undefined in that case.

--
Ben.
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      11-15-2011
Ben Bacarisse <> writes:
> Keith Thompson <kst-> writes:
>> Ben Bacarisse <> writes:
>>> jacob navia <> writes:

>> [...]
>>>> You will agree that *p is the same as p[0] isn't it? For ALL p?
>>>
>>> Kinda. It's true in the sense that you get the same result when the
>>> result is defined, and that both are undefined if either one is
>>> undefined. Are two "undefined behaviours" always "the same"? A thread
>>> about that could run and run.

>>
>> And it's not true of p is a function pointer.

>
> Surely both *p and p[0] are undefined in that case.


*p is simply a function designator, with the same meaning as p (C99
6.3.2.1p4). It's sometimes used to make it clear to the reader that p
is the name of an object of pointer-to-function type, not the name of a
function.

p[0] is a constraint violation.

--
Keith Thompson (The_Other_Keith) kst- <http://www.ghoti.net/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      11-15-2011
On 11/15/2011 04:15 PM, Ben Bacarisse wrote:
> Keith Thompson <kst-> writes:
>
>> Ben Bacarisse <> writes:
>>> jacob navia <> writes:

>> [...]
>>>> You will agree that *p is the same as p[0] isn't it? For ALL p?
>>>
>>> Kinda. It's true in the sense that you get the same result when the
>>> result is defined, and that both are undefined if either one is
>>> undefined. Are two "undefined behaviours" always "the same"? A thread
>>> about that could run and run.

>>
>> And it's not true of p is a function pointer.

>
> Surely both *p and p[0] are undefined in that case.


If p is a function pointer, 6.5.3.2p4 seem to define *p quite nicely.
6.3.2.1p4 means that in most cases, *p is exactly equivalent to p,
rendering it rather pointless, but not undefined.

 
Reply With Quote
 
Willem
Guest
Posts: n/a
 
      11-15-2011
Ben Bacarisse wrote:
) Keith Thompson <kst-> writes:
)
)> Ben Bacarisse <> writes:
)>> jacob navia <> writes:
)> [...]
)>>> You will agree that *p is the same as p[0] isn't it? For ALL p?
)>>
)>> Kinda. It's true in the sense that you get the same result when the
)>> result is defined, and that both are undefined if either one is
)>> undefined. Are two "undefined behaviours" always "the same"? A thread
)>> about that could run and run.
)>
)> And it's not true of p is a function pointer.
)
) Surely both *p and p[0] are undefined in that case.

Well, technically what you just said is meaningless, you see,
'undefined' is not a value that a variable can take in C.

Pedantically, it is 'undefined behaviour' to access both these values.
The behaviour need not be the same for those two cases. But then again, it
need not be the same when you compare the first case with the first case.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: A portable stack in C blmblm@myrealbox.com C Programming 20 11-22-2011 09:31 AM
C/C++ compilers have one stack for local variables and return addresses and then another stack for array allocations on the stack. Casey Hawthorne C Programming 3 11-01-2009 08:23 PM
stack frame size on linux/solaris of a running application stack Surinder Singh C Programming 1 12-20-2007 01:16 PM
Portable Python - free portable development environment ! perica.zivkovic@gmail.com Python 7 01-13-2007 11:19 AM
portable (VHDL) vs. non-portable (altera LPM) approaches to signed computations Eli Bendersky VHDL 1 03-01-2006 02:43 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57