(defvar *some-list* '(1 2))
That is, the address of the start of the list object (1 2) (or first
"cons cell")?
> Is there a way to get the address of a Lisp object?
Not portably. The only case where you'd be likely to want such a thing
is in the context of some kind of FFI, and there will then generally be
something specified by the FFI which will give you whatever address is
meaningful to the language you're talking to (and presumably deal with
making sure the object does not get relocated at some inconvenient
time).
No defined way in the ANSI Standard for CL, no.
However, most implementations have some undocumented internal facility
that does it. E.g., CMUCL has the function KERNEL:GET-LISP-OBJ-ADDRESS:
cmu> (kernel:get-lisp-obj-address 3)
12
cmu> (kernel:get-lisp-obj-address (list 1))
1218161251
cmu> (format nil "~x" *)
"489BAA63"
cmu>
Note that CMUCL uses "low-tagging" of objects, so you have to zero the
low three bits of the return values from KERNEL:GET-LISP-OBJ-ADDRESS
to get the actual memory address of the Lisp object.[1]
-Rob
[1] For objects that *have* memory addresses; not all do
[e.g., fixnums, characters, a few others].
-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
What do you need this for? Maybe there is some better way to achieve
what you actually want...
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Well, at the portable lisp level, *SOME-LIST* is bound to the start of
the list object (1 2). It is a pointer to the first cons cell in that
list. You can get to the second cons cell with (CDR *SOME-LIST*), which
should be all you really need.
A physical machine address is not generally useful because most Common
Lisp systems now use relocating or compacting garbage collectors, so the
objects are subject to movement during GC, so the physical machine
address can change over time. That makes it not particularly useful.
As others have noted, there are often ways to get that information in a
non-portable manner, as well as ways in some lisp systems to "pin" the
lisp object so that the GC won't move it.
--
Thomas A. Russ, USC/Information Sciences Institute
Not standard, but:
(defun object-internal-pointer (object)
"Given an arbitrary object, return an integer (should be fixnum in
many cases, unknown how Xerox and Gold Hill deal with this) which is
unique to that object, generally its actual pointer address"
(flet ((abut (x y)
(+ y (* x (expt 10 (1+ (floor (log y 10))))))))
(or
#+CCL (ccl::%address-of object)
#+CMU (kernel:get-lisp-obj-address object)
#+SBCL (sb-kernel:get-lisp-obj-address object)
;; (sb!kernel:get-lisp-obj-address object)
#+LISPWORKS (sys:object-address object)
#+(AND EXCL (NOT (AND ALLEGRO-VERSION>= (VERSION>= 5 0))))
(excl::pointer-to-fixnum object)
#+(AND EXCL ALLEGRO-VERSION>= (VERSION>= 5 0))
(excl::pointer-to-address object)
#+SYMBOLICS (si:%pointer object)
#+CORAL (ccl::%ptr-to-int object)
#+GOLD-HILL (multiple-value-call #'abut (sys:%pointer object))
#+HP (prim:@inf object)
#+IBCL (si:address object)
#+KCL (si:address object)
#+LUCID (lucid::%pointer object)
#+PYR 0
#+TI (si:%pointer object)
#+VAXL (system::%sp-pointer->fixnum object)
#+XEROX (abut (il::\\hiloc object) (il::\\loloc object))
;; ECL? CORMAN?
(extract-pointer object))))
;;; If the above fails, something like this might work in your Lisp
(defun extract-pointer (object)
(values
(parse-integer
(with-output-to-string (s)
(print-unreadable-object (object s :identity t)))
:start 6
:radix 16.
:junk-allowed t)))
In ECL it's si:pointer that returns the address of the object as a
fixnum. Example
> (defparameter *x* 12)
*X*
> (si:pointer *x*)
51
> (si:pointer *y*)
The variable *Y* is unbound.
> (defparameter *y* *x*)
*Y*
> (si:pointer *y*)
51
> (defparameter *L* '(1 2 3))
*L*
> (si:pointer *L*)
31679961
When an object is immutable, it doesn't need a memory address.
You can just compare it instead.
--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
Furthermore, if the information needed to fully describe the object
will fit into the same size value as a memory pointer (including the
tag bits), then no actual memory needs to be allocated for the object
at all. Such Lisp values are often called "immediate" objects. Many
CL, Scheme, and other Lisp implementations (though not all) use tagged
pointers and have some subset of their objects represented as immediates.
Indeed, one of the main motivations in CL for the partition of the
integer type into fixnums and bignums is the assumption that fixnums
will be implemented as immediates.[1]
-Rob
[1] Well, at least implemented as something "more efficient" than bignums.
See CLHS "Issue FIXNUM-NON-PORTABLE:TIGHTEN-DEFINITION".
Fixnums and chars and so on are stored immediately in many
implementations, rather than being `boxed' and referred to by pointer.
They may not exist outside of a processor's register file, and therefore
truly have no address. Also, any value type for which the behaviour
of EQ is not well-defined doesn't have a well-defined `address' either
due to implicit copying.
Finally, it's not clear to me why knowing the machine address of a value
is especially useful, since the next time the garbage collector wakes up
the address can change anyway. Is the OP really looking for a locative
-- a value which encapsulated the ability to read and write a particular
place? (You can fake locatives portably in CL using GET-SETF-EXPANSION.)
-- [mdw]
Consider a C program like this:
int* foo (int x) { return &x; }
Now, ask yourself: is the pointer returned by foo the address of the
integer object?
Helmut
Thanks! Nice to see that it is possible to handle multiple
implementations through #+, whatever that is.
When I run it (SBCL 1.0.37 32-bit Windows) I get strange behavior for
ints. At first I thought it was like Python - which allocates memory
for ints over a small range (couple of hundred or so) and any
reference to say 2, points to where 2 is permanently located. However,
with SBCL I am seeing values all the way up into the millions that are
4 times the value of the int. It could be that the system knows that
certain address values actually refer to an int, so don't use the
address, just divide by 4, but then it would have to somehow know the
type of the object before using the address. And I would think the
type is stored with the object, not the symbol.
"SBCL 1.0.37"
"*SOME-LIST*" location 602199403
location first cdr: 602199411
location second cdr: 602199419
location "*SOME-INT*" value 0: 0
location "*SOME-INT*" value 1: 4
location "*SOME-INT*" value 2: 8
location "*SOME-INT*" value -1: 4294967292
location "*SOME-INT*" value 50: 200
location "*SOME-INT*" value 500000000: 2000000000
location "*SOME-STRING*" value "": 602235335
location "*SOME-STRING*" value "hello": 602243695
location "*SOME-OTHER-STRING*" value "": 602249687
It is surprising that the "CONS cells" appear to be 8 bytes apart.
Isn't there type information that has to go with an object?
"Note that append copies the top-level list structure of each of its
arguments except the last."
and didn't want to do it with eq.
I also don't understand why append would operate that way. If I have a
list A and list B - joining them together could either leave the "top-
level list structure of each" unchanged, or both copied. I don't
understand why I would want A copied and B left unchanged.
(devar *some-int* 2)
Where does the 2 get stored so that I can reference it 2000 lines
later? If it goes in the slot where an address would normally go, how
does the system no it is a value and not an address?
Can you store type information where addresses (pointers) normally go,
and not negatively impact how much memory you can use on a 32-bit
implementation?
> However, with SBCL I am seeing values all the way up into the millions
> that are 4 times the value of the int. It could be that the system
> knows that certain address values actually refer to an int, so don't
> use the address, just divide by 4, but then it would have to somehow
> know the type of the object before using the address. And I would
> think the type is stored with the object, not the symbol.
Actually the type is stored in the "address" modulo 4, i.e. the lower
two bits. In other words, the object (the integer) is stored wholly
within the "address" value (called a "word" in lisp lingo), and it
doesn't really point to anything.
--
Frode V. Fjeld
> If it goes in the slot where an address would normally go, how does
> the system no it is a value and not an address?
An implementation is free to do this however it wants, but usually this
information is encoded in the last few (least significant) bits of the
"address". This means that heap-allocated objects can only exist at
certain byte boundaries--for example if the three least significant bits
are used for type information, objects must be 8-byte aligned.
--
Frode V. Fjeld
Thanks!
Yes.
int* foo (int x)
{
int *a = &x;
printf("value of x: %d\n", x);
printf("value a points to->%d\n", *a);
printf("value of a: %d value of &x: %d\n", a, &x);
return &x;
)
int main (const int argc, const char *argv[])
{
foo(10);
}
-------------------------- output --------------------------------
value of x: 10
value a points to->10
value of a: 1638236 value of &x: 1638236
It's the typical way to append two lists. Here's a straightforward
recursive implementation for two lists that shows why the result has the
second argument as a tail.
(defun append2 (list1 list2)
(if (endp list1) list2
(cons (first list1)
(append (rest list1) list2))))
For each cons cell in list1 there is a call to cons---list1 is copied,
but list2 will serve as a perfectly good tail for the resulting list.
The generalization of append2 is a right associative appendN, e.g.,
(appendN w x y z) == (append2 w (append2 x (append2 y z)))
^^^^^^^^^^^^^
copies y, but not z
^^^^^^^^^^^^^^^^^^^^^^^^^
copies x, but not (ap2 y z)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
copies w, but not (ap2 x (ap2 y z))
So the result of appendN copies the list structure of all its arguments
but the last. It's worth noting that this type of manipulation is
fairly common---NCONC leaves its last argument unchanged, REVAPPEND
doesn't copy its last argument, NRECONC doesn't modify its last
argument, &c.
//JT
Thanks for the explanation. But why isn't append implemented by just
connecting the existing lists?
>>
>>
>> >> [1] For objects that *have* memory addresses; not all do
>> >> [e.g., fixnums, characters, a few others].
>>
>> > How does an object not have a memory address??
>>
>> Consider a C program like this:
>>
>> int* foo (int x) { return &x; }
>>
>> Now, ask yourself: is the pointer returned by foo the address of the
>> integer object?
>>
>
> Yes.
>
> int* foo (int x)
> {
> int *a = &x;
> printf("value of x: %d\n", x);
> printf("value a points to->%d\n", *a);
> printf("value of a: %d value of &x: %d\n", a, &x);
> return &x;
> )
>
>
> int main (const int argc, const char *argv[])
> {
> foo(10);
> }
You are not understanding.
For this discussion, it doesn't matter what foo does, but what is done
with its result:
------------------------------------------------------------------------
#include <stdio.h>
int* foo (int x){
return &x;
}
void bar(){
int* pointer_to_so_called_x=foo(42);
printf("value of x: %d\n",*pointer_to_so_called_x);
printf("value of x: %d\n",*pointer_to_so_called_x);
}
int main(){
bar();
return(0);
}
------------------------------------------------------------------------
-*- mode: compilation; default-directory: "/tmp/" -*-
Compilation started at Wed Dec 1 23:07:09
SRC="/tmp/p.c" ; EXE="p" ; gcc -I. -L. -g3 -ggdb3 -o ${EXE} ${SRC} && ./${EXE} && echo status = $?
/tmp/p.c: In function 'foo':
/tmp/p.c:4: warning: function returns address of local variable
value of x: 42
value of x: 32688
status = 0
Compilation finished at Wed Dec 1 23:07:09
The version that connects the two lists in-place (by modifying the last
pointer of the first list) is called NCONC. Append is for when you
don't want to modify the two original lists:
(defvar foo '(1 2 3))
(defvar bar '(4 5 6))
(defvar foobar (append foo bar)) ; set foobar to (1 2 3 4 5 6)
If you used nconc instead of append, then "foo" would get (maybe
unexpectedly) changed to (1 2 3 4 5 6).
I'm not much of a lisper these days but I think the mutating functions
like nconc, nreverse, etc. have gone out of style, now that machines are
less memory starved than in the old days. I don't mean they're never
used or never necessary, but it's cleaner to program in a mutation-free
style unless you've got a concrete reason to do otherwise.
The question was "Now, ask yourself: is the pointer returned by foo
the address of the
integer object?"
You are dealing with issues outside of that question.
For what it is worth, on the Windows version of Tiny C, 42 is printed
each time. What would happend between the first printf and the second,
to cause your run to have a different value printed the second time?
After the function returns there IS NO valid integer object pointed to.
"a" is just a stray pointer that contains a memory address.
> What would happend between the first printf and the second,
> to cause your run to have a different value printed the second time?
It could be clobbered by a different function call, including something
like an interrupt service routine that is completely invisible to the
caller, or a function in another thread under pre-emptive concurrency.
Or even in a single threaded system with no interrupts, the OS could
reclaim the memory below the stack pointer after the function returns,
possibly even causing the program to crash outright when you dereference
the pointer. I'm not completely sure that the C standard allows that,
but it seems perfectly reasonable.
>>
>> You are not understanding.
>>
>> For this discussion, it doesn't matter what foo does, but what is done
>> with its result:
>
> The question was "Now, ask yourself: is the pointer returned by foo
> the address of the
> integer object?"
>
> You are dealing with issues outside of that question.
Not at all. You still don't understand.
> For what it is worth, on the Windows version of Tiny C, 42 is printed
> each time. What would happend between the first printf and the second,
> to cause your run to have a different value printed the second time?
That's the point. The variable x doesn't exist anymore when foo exits.
Therefore foo cannot return a pointer to x. Since what foo returned is
not a pointer to its variable x, it is something else. And since it is
something else, anything else can happen.
> Actually the type is stored in the "address" modulo 4, i.e. the lower
> two bits. In other words, the object (the integer) is stored wholly
> within the "address" value (called a "word" in lisp lingo), and it
> doesn't really point to anything.
Not quite. The type is stored in the bottom /three/ bits, but there are
two distinct fixnum types: 0 for even fixnums and 4 for odd fixnums.
The upshot is that you get 30 bits for fixnums even though you have
three tag bits. There are other benefits to this representation:
* You can do addition and subtraction directly on these values,
because the factor of four distributes over the addition; overflow
is indicated by processor status flags in the usual way.
* You can do division if you shift the quotient left two bits
afterwards. You can do multiplication if you shift one of the
operands right by two bits beforehand.
* Extra bonus: fixnums are already a multiple of the word size, so
they can be used as-is when indexing vectors of descriptors.
Descriptors which are pointers won't contain word-aligned addresses, but
on most systems you can compensate for the type tag for free by
specifying an immediate constant offset. Pointers will only have eight
byte granularity (because you have three tag bits) cons cells are two
words and most other objects are bigger so it doesn't matter too much.
The main loss is in cellification, because cells only need to be a
single word, if the implementation does that. Cellification is the
technique of replacing a variable which is (a) assigned and (b) captured
by an escaping closure with a (nonmutable) reference to a heap-allocated
cell containing the value. If you do this then you can construct
closures by simply copying the captured variables, which avoids
extending value lifetimes unnecessarily and eliminates indirection when
referencing lexical variables from enclosing scopes.
-- [mdw]
...and never use them on quoted lists! ;)
That's silly, because eq does exactly what you need here.
But we are in the 64 bit era now. Does the tag format stay the same?
SBCL uses four tag bits in the 64-bit compiler. Again, two tags are
taken for fixnums, one for odd and one for even, so fixnums have 61 bits
of precision, and can be used directly to index arrays.
-- [mdw]
I think this point gets made every time these sorts of questions come
up, but there are plenty of places where it still makes sense to use
these sorts of functions, and doesn't make much sense to not use them.
The typical example is in building a list by PUSHing values in some sort
of iteration and NREVERSEing the list afterward. E.g., a simple
implementation of REMOVE-IF-NOT:
(defun our-remove-if-not (predicate list &aux (result '()))
(dolist (x list (nreverse result))
(when (funcall predicate x)
(push x result))))
(Of course, this isn't the only way to write this function. We could
also use a tail pointer to incrementally add to the list at the end in
constant time, ...) To use REVERSE here rather than NREVERSE here
conses up a new list unnecessarily---those cons cells won't be
accessible anywhere else, so why not use the same ones? Of course, it's
also possible that an implementation is such that consing up the new
list and garbage collecting the old one will be fine too. NREVERSE
isn't /required/ to do a destructive modification, but allows it.
NREVERSE and friends give the implementation more freedom to make
(hopefully smart) decisions about what to do.
//JT
Yes, that is a standard example and would count as a concrete reason.
The hackiness is also mitigated by being very localized, in that
particular code.
To use REVERSE here rather than NREVERSE here conses up a new list
unnecessarily---those cons cells won't be accessible anywhere else,
so why not use the same ones?
I wonder whether NREVERSE might make the cache locality worse in an
implemetation with a copying garbage collector, since the reversed list
would be at decreasing addresses while everything else allocated at
around the same time would be at increasing addresses. Of course it
will all (if still around) get copied at the next gc anyway, so it won't
matter after that.
I do get the impression that in most modern applications, gc doesn't
burn that much of the cpu cycles. In other words, consing is cheap.
But this was my point about the freedom that an implementation has with
NREVERSE. This is a fairly standard idiom and hopefully detectable. If
the copying REVERSE will have better performance for some reason (e.g.,
cache locality, as you mentioned) the implementation is free to to do a
copying reverse with NREVERSE, since destructive modifications aren't
mandated, just permitted.
//JT
Hmm, according to
http://www.lispworks.com/documentation/HyperSpec/Body/f_revers.htm#nreverse
you're right, mutation is optional. I was surprised to hear that.
There's certainly an observable difference between the two versions.
It does look like nconc is required to be destructive.
On the contrary. My comprehension of the matter is solid.
>
> > For what it is worth, on the Windows version of Tiny C, 42 is printed
> > each time. What would happen between the first printf and the second,
> > to cause your run to have a different value printed the second time?
>
> That's the point. The variable x doesn't exist anymore when foo exits.
> Therefore foo cannot return a pointer to x.
We are not concerned with whether foo() is correct or not. The only
question asked regarding foo was whether the value returned contained
the address of the integer parameter.
> Since what foo returned is
> not a pointer to its variable x, it is something else. And since it is
> something else, anything else can happen.
"Anything can happen" is much too strong for the tiny program in
question.
>>
>>
>> >> [1] For objects that *have* memory addresses; not all do
>> >> [e.g., fixnums, characters, a few others].
>>
>> > How does an object not have a memory address??
>>
>> Consider a C program like this:
>>
>> int* foo (int x) { return &x; }
>>
>> Now, ask yourself: is the pointer returned by foo the address of the
>> integer object?
>>
>
> Yes.
[...]
> -------------------------- output --------------------------------
> value of x: 10
> value a points to->10
> value of a: 1638236 value of &x: 1638236
So the address of the integer object 10 is 1638236?
Helmut
> On Dec 1, 2:54 pm, "Pascal J. Bourguignon" <p...@informatimago.com>
> wrote:
> > TheFlyingDutchman <zzbba...@aol.com> writes:
> >
> > >> You are not understanding.
> >
> > >> For this discussion, it doesn't matter what foo does, but what is done
> > >> with its result:
> >
> > > The question was "Now, ask yourself: is the pointer returned by foo
> > > the address of the
> > > integer object?"
> >
> > > You are dealing with issues outside of that question.
> >
> > Not at all. You still don't understand.
>
> On the contrary. My comprehension of the matter is solid.
No it isn't. Try this:
int* foo (int x) { return &x; }
void baz (int x) {}
void bar (float x) {}
main() {
int* p = foo(1);
printf("%d\n", *p);
baz(2);
printf("%d\n", *p);
bar(3.141);
printf("%d\n", *p);
}
rg
If your answer to the question is no, I would say that is an incorrect
answer.
>
> > What would happend between the first printf and the second,
> > to cause your run to have a different value printed the second time?
>
> It could be clobbered by a different function call, including something
> like an interrupt service routine that is completely invisible to the
> caller, or a function in another thread under pre-emptive concurrency.
> Or even in a single threaded system with no interrupts, the OS could
> reclaim the memory below the stack pointer after the function returns,
> possibly even causing the program to crash outright when you dereference
> the pointer. I'm not completely sure that the C standard allows that,
> but it seems perfectly reasonable.
I should have asked what did happen in the tiny C program that Pascal
B. displayed. Such a small singled threaded program, the output
difference of the two printf's really is curious.
You can say anything you want, but that doesn't mean that what you say
is right.
> I should have asked what did happen in the tiny C program that Pascal
> B. displayed. Such a small singled threaded program, the output
> difference of the two printf's really is curious.
There is nothing curious about it. If you think it is curious, all you
are showing is that you don't know C. It is completely normal and
expected behavior, given how C works. The code says:
int* foo (int x){
return &x;
}
During the execution of this function, &x is the address of a slot in
foo's stack frame. After the function returns, the stack frame is no
longer active, and the return value points into unallocated memory that
potentially contains garbage. printf is a complicated function that
does some stack allocation during its own execution, and overwrites that
slot sometime after first accessing it. E.g. printf works something
like:
n = get_arg(); /* gets 42 */
s = write_as_decimal (n); /* pushes stuff on stack during
execution */
calling write_as_decimal pushes stuff on the stack that clobbers the
location where n was retrieved from. So the contents change between the
two printf calls. In other words, the pointer was invalid when it got
passed to printf, and "anything can happen" precisely describes the
situation.
You could conclude from this that C is a language with dangerous sharp
edges, and you'd be right. Lisp is much safer than C in this regard.
Anyway, I think your question has been answered accurately by several
people by now. Whether you choose to accept the answers is up to you.
> and didn't want to do it with eq.
Why not? EQ is pretty much exactly "compare these two addresses and say
if they are the same", except it will deal with the GC moving things.
> On Dec 1, 2:54 pm, "Pascal J. Bourguignon" <p...@informatimago.com>
> > That's the point. The variable x doesn't exist anymore when foo exits.
> > Therefore foo cannot return a pointer to x.
>
> We are not concerned with whether foo() is correct or not. The only
> question asked regarding foo was whether the value returned contained
> the address of the integer parameter.
And you were told, quite clearly, that since the parameter no longer
exists, it doesn't have an address, so `foo' certainly can't return it.
> > Since what foo returned is not a pointer to its variable x, it is
> > something else. And since it is something else, anything else can
> > happen.
>
> "Anything can happen" is much too strong for the tiny program in
> question.
Wrong. In C, this is what the standard calls `undefined behaviour',
which it defines as
behavior, upon use of a nonportable or erroneous program
construct, of erroneous data, or of indeterminately valued
objects, for which this International Standard imposes no
requirements
`No requirements': anything can happen.
* Your program might crash.
* Your disks might be erased.
* Custard might emerge from your computer's USB ports.
* Demons might fly out of your nose.
* The missiles might be launched.
Again, if you're very lucky, these things might happen /after/ you
execute your erroneous program. The C standard doesn't guarantee that
causality remains in force, though, so it seems legitimate that these
things occur preemptively.
-- [mdw]
> "Anything can happen" is much too strong for the tiny program in
> question.
I don't know the spec well enough, but I'd strongly assume this is an
"undefined behaviour" case (or "is an error"), so presumably yes,
anything could happen.
> The C standard doesn't guarantee that
> causality remains in force, though, so it seems legitimate that these
> things occur preemptively.
We could probably rely on physics to ensure causality, although last
time I knew anything about it, which is now a long time ago, it was not
known whether causality-violoating things could eveolve from
physically-plausible initial conditions: I think everyone assumed they
could not in the same way they assumeed cosmic censorship (I have no
idea if that's been proved now either, though I'd assume not).
(let this not serve as a cue for some futile philosophical debate on
what causality is, I'm just making a joke.)
Yes. Do a web search for "low-tagged pointers"...
-Rob
-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
> I want to verify this statement from CLtL2:
>
> "Note that append copies the top-level list structure of each of its
> arguments except the last."
>
> and didn't want to do it with eq.
Use EQL then.
> I also don't understand why append would operate that way.
Because it's useful.
> If I have a list A and list B - joining them together could either
> leave the "top- level list structure of each" unchanged, or both
> copied. I don't understand why I would want A copied and B left
> unchanged.
Because B doesn't have to be a proper list -- and because it's faster
like this. A program which isn't mutating the lists doesn't care either
way (but can't call NCONC because that will mutate them); a program
which wants to copy the tail can use COPY-LIST, or CONCATENATE rather
than APPEND.
-- [mdw]
Right back at ya.
>
> > I should have asked what did happen in the tiny C program that Pascal
> > B. displayed. Such a small singled threaded program, the output
> > difference of the two printf's really is curious.
>
> There is nothing curious about it. If you think it is curious, all you
> are showing is that you don't know C. It is completely normal and
> expected behavior, given how C works. The code says:
>
> int* foo (int x){
> return &x;
> }
>
> During the execution of this function, &x is the address of a slot in
> foo's stack frame. After the function returns, the stack frame is no
> longer active, and the return value points into unallocated memory that
> potentially contains garbage. printf is a complicated function that
> does some stack allocation during its own execution, and overwrites that
> slot sometime after first accessing it. E.g. printf works something
> like:
>
> n = get_arg(); /* gets 42 */
> s = write_as_decimal (n); /* pushes stuff on stack during
> execution */
>
> calling write_as_decimal pushes stuff on the stack that clobbers the
> location where n was retrieved from. So the contents change between the
> two printf calls. In other words, the pointer was invalid when it got
> passed to printf, and "anything can happen" precisely describes the
> situation.
Now that is a plausible explanation. Thanks.
>
> You could conclude from this that C is a language with dangerous sharp
> edges, and you'd be right. Lisp is much safer than C in this regard.
My conclusion is that using C code to try to explain something in
Common Lisp is at best, ill-advised.
>
> Anyway, I think your question has been answered accurately by several
> people by now. Whether you choose to accept the answers is up to you.
My question was regarding how to get the address of a Lisp object. It
was answered in superb fashion with an great function by Andrew
Philpot. Additionally I had a question regarding how an object in Lisp
could not have an address. Rob Warnock did an excellent job explaining
that some Lisp implementations use address bits to specify types and
then store the value in the rest of the address bits. Helmut Eller
asked as question regarding some C code. I will let him comment on the
quality of your answer to his question, and the answers offered by
others.
It's just my personal preference to see memory addresses printed out,
rather than run code looking for equality. But you and Thomas Russ do
point out something I had not thought about regarding the garbage
collector moving objects. I don't know what threshold or thresholds
kick in the garbage collector. Buy my programs for learning are tiny
and I have to guess they won't trigger it.
> Buy my programs for learning are tiny
> and I have to guess they won't trigger it.
That's not a safe assumption to make, to put it mildly.
And I responded, with clarity, that what happens after foo exits, is
outside the scope of the question. foo takes the address of the int
parameter and then returns it. Talking about what happens after foo
exits is answering a question that wasn't asked.
>
> > > Since what foo returned is not a pointer to its variable x, it is
> > > something else. And since it is something else, anything else can
> > > happen.
>
> > "Anything can happen" is much too strong for the tiny program in
> > question.
>
> Wrong. In C, this is what the standard calls `undefined behaviour',
> which it defines as
>
> behavior, upon use of a nonportable or erroneous program
> construct, of erroneous data, or of indeterminately valued
> objects, for which this International Standard imposes no
> requirements
>
> `No requirements': anything can happen.
>
> * Your program might crash.
> * Your disks might be erased.
> * Custard might emerge from your computer's USB ports.
> * Demons might fly out of your nose.
> * The missiles might be launched.
>
> Again, if you're very lucky, these things might happen /after/ you
> execute your erroneous program. The C standard doesn't guarantee that
> causality remains in force, though, so it seems legitimate that these
> things occur preemptively.
>
But we aren't talking about an infinite number of abstract programs,
or an infinite number of hypothetical runs. We are talking about a
specific run of a specific small program. There is only a small number
of things that could have caused the results, regardless of what
someone wrote in a specification. Paul Rubin gave what is likely the
correct answer - after reading memory, printf makes function calls
that utilize the same stack memory as was previously read.
> > And you were told, quite clearly, that since the parameter no longer
> > exists, it doesn't have an address, so `foo' certainly can't return it.
>
> And I responded, with clarity, that what happens after foo exits, is
> outside the scope of the question. foo takes the address of the int
> parameter and then returns it.
You can't have it both ways. Either you can talk about returning
something, or you can restrict attention to what happens before the
function exits; but trying to do both simultaneously is simply
inconsistent.
> > `No requirements': anything can happen.
>
> But we aren't talking about an infinite number of abstract programs,
> or an infinite number of hypothetical runs. We are talking about a
> specific run of a specific small program.
And that /specific small program/ has undefined behaviour.
> There is only a small number of things that could have caused the
> results, regardless of what someone wrote in a specification.
No. Compilers are at liberty to detect programs which exhibit undefined
behaviour and cause arbitrary effects either at compile time (if the
undefined behaviour is unconditional) or at runtime.
-- [mdw]
> No. Compilers are at liberty to detect programs which exhibit undefined
> behaviour and cause arbitrary effects either at compile time (if the
> undefined behaviour is unconditional) or at runtime.
And, of course, that would actually be a good thing to do (well, not
arbitrary effects). A compiler which said "I won't compile that" would
be useful and a compiler which generated code which caused a fault at
runtime would also be useful (albeit you'd probably prefer the former),
even in the case where nothing uses the value.
Which really show you don't understand. At all.
When we consider results returned by functions, we consider them outside
of that function!
Nothing you can do inside the functions matters, at all, for things
returned by them. (It matters how the functions find or build the
things they return, for that will define what is returned, but how they
do it, or what else the do, doesn't matter, to the being of the things
returned and their ulterior use, which is the point of this discussion).
--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
Well, time-directed causality is doubtful: http://dbem.ws/FeelingFuture.pdf
> My conclusion is that using C code to try to explain something in
> Common Lisp is at best, ill-advised.
Indeed.
You are mistaken. The question you posed was:
> How does an object not have a memory address??
which the code above illustrates nicely IMHO.
But there is an even more direct answer to your question: there are many
ways an object can not have a memory address (with emphasis on the
singular article "a"):
1. It can be stored in a machine register
2. It can be stored in multiple places in memory
3. It can be stored in off-line storage
4. It can be moveable (which is common in garbage-collected systems)
rg
> Well, time-directed causality is doubtful: http://dbem.ws/FeelingFuture.pdf
I think I'll believe in observable causality violation when I see
people getting rich from it (money pours out in a fairly dramatic way
if you do some obvious tricks).
The issue is that the caller never receives the address of the int
parameter. It receives the -former- address of an int parameter, that
has since changed into the address of an unallocated memory location.
> But you and Thomas Russ do
> point out something I had not thought about regarding the garbage
> collector moving objects. I don't know what threshold or thresholds
> kick in the garbage collector. Buy my programs for learning are tiny
> and I have to guess they won't trigger it.
This is in lispworks:
CL-USER 79 > (let* ((l (list 1 2 3))
(a (make-array 1000 :element-type 'fixnum))
(rand-index (random 1000)))
(format t "the current address of the list l is: ~a~%"
(sys:object-address l))
(format t "the value of the second element of the list l
is: ~a~%"
(second l))
(format t "now setting each element of the array a to
its index.~%")
(dotimes (n 1000) (setf (aref a n) n))
(format t "the value of element ~a of the array a is: ~a~%"
rand-index (aref a rand-index))
(setf a nil)
(format t "the current address of the list l is: ~a~%"
(sys:object-address l)))
the current address of the list l is: 275148415280
the value of the second element of the list l is: 2
now setting each element of the array a to its index.
the value of element 994 of the array a is: 994
the current address of the list l is: 279173174832
NIL
Note that this doesn't show the address of l changing every single time
you execute this simple bit of code, but it does happen often enough
that you cannot assume that objects don't move, even in simple code.
warmest regards,
Ralph
--
Raffael Cavallaro
> On Dec 1, 2:54 pm, "Pascal J. Bourguignon" <p...@informatimago.com>
> wrote:
> > TheFlyingDutchman <zzbba...@aol.com> writes:
> >
[...]
> > > For what it is worth, on the Windows version of Tiny C, 42 is printed
> > > each time. What would happen between the first printf and the second,
> > > to cause your run to have a different value printed the second time?
> >
> > That's the point. The variable x doesn't exist anymore when foo exits.
> > Therefore foo cannot return a pointer to x.
>
> We are not concerned with whether foo() is correct or not. The only
> question asked regarding foo was whether the value returned contained
> the address of the integer parameter.
>
If it does, it's by luck and not design. Pascal is right; once the
function returns, x is undefined and the functions return value is
likewise undefined.
> > Since what foo returned is
> > not a pointer to its variable x, it is something else. And since it is
> > something else, anything else can happen.
>
> "Anything can happen" is much too strong for the tiny program in
> question.
No, it's not. I could tell you horror stories of bugs due to this very
issue.
I didn't say that causality was violated, and these experiments are just
experiments.
I added the "time-directed" qualifier to note that there could be a kind
of causality that doesn't imply a direction in time, as is usually
assumed.
An effect in the past could be caused by a cause in the future.
It would be a kind of time travel.
> Thanks! Nice to see that it is possible to handle multiple
> implementations through #+, whatever that is.
It is a reader macro that conditionally reads or skips forms based on
values in *features* (and with some implemenation dependent extensions
as in the case of Allegro).
> When I run it (SBCL 1.0.37 32-bit Windows) I get strange behavior for
> ints. At first I thought it was like Python - which allocates memory
> for ints over a small range (couple of hundred or so) and any
> reference to say 2, points to where 2 is permanently located. However,
> with SBCL I am seeing values all the way up into the millions that are
> 4 times the value of the int.
This is precisely the consequence of the discussion of "immediate"
values -- values that don't have a memory address because they are not
stored in memory.
It is also a consequence of one of the often used lisp implementation
techniques for tagging integer (fixnum) values in a way that allows for
efficient arithmetic. The magnitude of a fixnum is shifted by a set of
low-order tag bits which are cleverly chosen to allow addition and
subtraction to work properly on a tagged value without a need to mask or
shift the value. In this scheme the fixnum tags are typically 000 and
100 for even and odd fixnums. But that is likely more detail than you
want.
--
Thomas A. Russ, USC/Information Sciences Institute
> I want to verify this statement from CLtL2:
>
> "Note that append copies the top-level list structure of each of its
> arguments except the last."
>
> and didn't want to do it with eq.
Well, EQ is the official and portable way to do it.
If you use the addresses, you have to be careful that you don't do
anything in the middle of gathering your addresses that could trigger a
garbage collection, since that could move the physical address and
render your test invalid.
So, anything that creates new objects (conses, non-immediate numbers,
arrays, etc.) could potentially trigger a GC and move things. So you
would have to be very careful. It may even be possible in systems that
have a multi-threaded GC implementation that a GC can occur even without
any overt memory allocation.
> I also don't understand why append would operate that way.
Because it is useful and efficient (especially space efficient) to do it
that way.
> If I have a
> list A and list B - joining them together could either leave the "top-
> level list structure of each" unchanged,
Actually, it can't do this. If you leave the top-level list structure
of each unchanged, then they won't be joined because the first list will
have the same structure it had before the joining.
> or both copied. I don't
> understand why I would want A copied and B left unchanged.
Because that is the minimal amount of copying that will allow a
non-destructive concatenation of the two lists. This minimizes both
time and space usage, since you don't have to copy the last argument's
list structure.
> I added the "time-directed" qualifier to note that there could be a kind
> of causality that doesn't imply a direction in time, as is usually
> assumed.
>
> An effect in the past could be caused by a cause in the future.
> It would be a kind of time travel.
That's what I mean by causality violation, pretty much.
The reason I was thinking about this recently[*] is that it turns out
some serious science-fiction authors who want to write space operas
with FTL travel (which you mostly need for any kind of decent space
opera) have tried to think through some of the consequences, of which
the most serious is obviously CV. Charlie Stross has interesing stuff
on this (having written such a space opera), and as he says "accepting
causality violation means computing with closed timelike curves or, in
simpler terms, really strong deterministic solutions to P=NP, and then
some".
--tim
[*] I have some background interest as my never-completed (in fact
never properly started) PhD topic was in a related area, but as I said
it's now a very long time ago and what little I knew I have mostly
forgotten.
> Hmm, according to
>
> http://www.lispworks.com/documentation/HyperSpec/Body/f_revers.htm#nreverse
>
> you're right, mutation is optional. I was surprised to hear that.
> There's certainly an observable difference between the two versions.
Well, only if one of them is destructive. If NREVERSE were defined as
(defun nreverse (list) (reverse list))
then there would not be any outwardly observable differences.
> It does look like nconc is required to be destructive.
Yes. Certain destructive functions can be relied upon. Others not.
DELETE versus REMOVE is one of the potential gotchas, since in the case
of returning NIL, it cannot be destructive.
Oh yeah. That too. :-)
rg
What I mean by observable difference is that you can tell whether
nreverse is destructive or not:
(setf x (list 1 2 3))
(setf y (nreverse x))
(print x)
According to the standard, the behavior of the above code is
implementation-dependent.
I have been consistent in talking about what the function returns -
not what it returned.
Team C-examples-answer-Lisp-questions is who can't have it both ways.
You can't say you are proving a C "object" doesn't have a memory
address, by claiming that the parameter to foo doesn't have a memory
address - based on the fact that - the parameter to foo doesn't exist.
It either exists with a memory address, exists without a memory
address, or doesn't exist. You can't claim that something doesn't
exist, and also doesn't have a memory address. Well I suppose you
could make that claim, but then it would be better just to say it with
an example like -> "int myVar = 2;", after the computer is turned
off, the "object" myVar no longer has a memory address.
>
> > > `No requirements': anything can happen.
>
> > But we aren't talking about an infinite number of abstract programs,
> > or an infinite number of hypothetical runs. We are talking about a
> > specific run of a specific small program.
>
> And that /specific small program/ has undefined behaviour.
We aaren't talking about a source code file. We are talking about an
executable file, that was compiled by a specific C implementation that
compiles in specific ways. And we don't have undefined behaviour, else
Paul Rubin wouldn't have gotten the reason the printf's changed values
in his second explanation for why it happened (assuming it did).
>
> > There is only a small number of things that could have caused the
> > results, regardless of what someone wrote in a specification.
>
> No. Compilers are at liberty to detect programs which exhibit undefined
> behaviour and cause arbitrary effects either at compile time (if the
> undefined behaviour is unconditional) or at runtime.
>
We aren't talking about compilers. We are talking about a compiler.
If you consider things outside of the function you are in worse shape.
If the "object" int that was passed to foo does not exist outside of
foo, then it is completely worthless in the ludicrous exercise of
using C code to show how a Common Lisp object can exist without a
memory address. In order to show something regarding an object, you
need an object. Or could I prove to you that man does not need food
and water to live by showing you a dead man who no longer eats and
drinks?
>
> Nothing you can do inside the functions matters, at all, for things
> returned by them. (It matters how the functions find or build the
> things they return, for that will define what is returned, but how they
> do it, or what else the do, doesn't matter, to the being of the things
> returned and their ulterior use, which is the point of this discussion).
>
The point of this discussion is how a Lisp object can not have an
address. This side discussion resolves around this question: "Now, ask
yourself: is the pointer returned by foo the address of the integer
object?"
Again, if you say there is no address because there is no object, you
haven't proved anything. In order to prove you cn have an object
without an address, you first need an object. If you say there is an
object, then it clearly has an address and that was procured via the &
operator and returned via the return statement.
> Or could I prove to you that man does not need food
> and water to live by showing you a dead man who no longer eats and
> drinks?
Why go seek a dead one when we have good living men who don't eat or
drink:
http://www.foxnews.com/health/2010/05/10/update-holy-mans-weeks-food-water/
Actually the point of the discussion was to answer some questions that
you asked with the presumed goal of improving your understanding. Note
the asymmetry in the situation: the people responding understand this
stuff and you don't. The idea was for them to transfer some of their
understanding to you. Why are you arguing with them?
If the address is -former- then the object is -former-. A former
object cannot demonstrate a single thing regarding a current object
having or not having an address.
The issue was why did his second printf print differently than the
first.
>
> > > Since what foo returned is
> > > not a pointer to its variable x, it is something else. And since it is
> > > something else, anything else can happen.
>
> > "Anything can happen" is much too strong for the tiny program in
> > question.
>
> No, it's not. I could tell you horror stories of bugs due to this very
> issue.
Not saying that there is no bug, just that there aren't infinite
possible bugs -> "anything can happen".
> I have been consistent in talking about what the function returns -
And what it returns is an invalid address.
> We aaren't talking about a source code file. We are talking about an
> executable file,
We aren't? My mistake. Where was that mentioned?
> that was compiled by a specific C implementation that compiles in
> specific ways.
Then I'm sure that the specific implementation will have been named,
clearly and explicitly. Umm... where?
-- [mdw]
> Yes. Certain destructive functions can be relied upon. Others not.
> DELETE versus REMOVE is one of the potential gotchas, since in the case
> of returning NIL, it cannot be destructive.
? Of course, it can.
Nicolas
Note that in some calling conventions (e.g. stdcall and fastcall on x86)
parameters are passed via registers.
So normally the address of x is a register, for example, EAX.
But there is no numeric value for it. You cannot determine it in runtime.
Then if you write &x compiler will have to allocate room for object on
stack.
But note that value returned won't be address of object itself but it would
be address of some bytes in stack where x is temporarily placed.
When function exits this pointer would be still pointing to location on
stack, but there will be no integer variable x associated with it.
Later it might or might not be occupied with something else.
It is important to understand that there is a difference between concepts
implemented on programming language level -- variables, integers and so
on -- and their physical implementation in form of machine code.
There is no one-to-one correspondence between them.
Variable x exists for compiler, but in machine code there is no variable x,
there is no even integer object -- it just works with bytes.
And it is definitely not specific to Lisp, you can observe same behaviour in
C.
Your problem is that you do not understand how it works on low level but
you're still arguing with people who do.
If you really want to know, learn x86 assembler language.
Then come back to Lisp (or C), use DISASSEMBLE function (or ask assembly
dump from compiler of your choice) and see how it actually works.
Then you just won't have questions about object addresses because you will
understand how it really works.
And also:
6. The object has not yet been created.
Example:
int a = 5;
Object b has no address as it does not yet exist.
My mistake. In the future I will not rely on context. I was referring
only to a Lisp object. And I had only been thinking of Lisp objects
referred to by symbols.
Is it invalid at the time of the execution of the return statement?
>
> > We aaren't talking about a source code file. We are talking about an
> > executable file,
>
> We aren't? My mistake. Where was that mentioned?
Pascal B. had a program run and we were discussing his run and his
output.
>
> > that was compiled by a specific C implementation that compiles in
> > specific ways.
>
> Then I'm sure that the specific implementation will have been named,
> clearly and explicitly. Umm... where?
>
OK, we know it's gcc, but don't know which version of gcc. Perhaps
Pascal B. can do a "gcc --version".
-----------------------------------------------------------------------------------------------------------------------
-*- mode: compilation; default-directory: "/tmp/" -*-
Compilation started at Wed Dec 1 23:07:09
SRC="/tmp/p.c" ; EXE="p" ; gcc -I. -L. -g3 -ggdb3 -o ${EXE} ${SRC}
&& ./${EXE} && echo status = $?
/tmp/p.c: In function 'foo':
/tmp/p.c:4: warning: function returns address of local variable
value of x: 42
value of x: 32688
status = 0
Compilation finished at Wed Dec 1 23:07:09
My problem was solved a long time ago by Rob Warnock. This C
discussion is just a nerdy semantic battle to keep the juices flowing.
> On Dec 2, 10:20□am, w...@stablecross.com (Bob Felts) wrote:
> > TheFlyingDutchman <zzbba...@aol.com> wrote:
> >
> > If it does, it's by luck and not design. □Pascal is right; once the
> > function returns, x is undefined and the functions return value is
> > likewise undefined.
>
> The issue was why did his second printf print differently than the
> first.
Which I thought was very clearly explained. A pointer to a memory
location was returned, but that memory location was
unallocated. Unallocated memory is generally not cleared when it is
released. It just retains the last thing written to that location.
Subsequent activity (namely the first printf) happens to put different
content into that particular memory location.
But understanding this requires that you have a reasonable if only
general understanding of typical procedure calling conventions. These
happen to be C conventions, but the general idea applies to any
convention which uses a stack to hold function call frames and store
local variables.
One could get similar results by just generating a random number and
using that as a memory address and printing the contents. If you run
other code that writes to random memory addresses, then you will
sometimes see the value of the first address change. Now, with the
stack frames, the process is a bit more constrained, which means that it
is easier to get something else written into the particular part of
memory.
Now if you don't have this background, it will all appear somewhat
mysterious, but that should be a signal that you don't really understand
what is going on.
On a more global level, though, I would suggest that you NOT try to
build a model of lisp object manipulation at the level of machine code
and memory locations -- especially since there are often several
different ways to implement particular lisp constructs and behaviors.
Instead, you should try to understand the semantics of the operations at
the data structure level. So for cons trees and lists, you want to be
able to understand these at the "box and pointer" level and not at the
level of machine addresses. Especially since all of the manipulations
that you do using lisp function calls operate at that level.
> > > > Since what foo returned is
> > > > not a pointer to its variable x, it is something else. □And since it is
> > > > something else, anything else can happen.
> >
> > > "Anything can happen" is much too strong for the tiny program in
> > > question.
> >
> > No, it's not. □I could tell you horror stories of bugs due to this very
> > issue.
>
> Not saying that there is no bug, just that there aren't infinite
> possible bugs -> "anything can happen".
Well, this is an interesting philosophical point. Given that the space
of programs in most languages is effectively unbounded, it would seem to
follow that the set of incorrect programs is also unbounded. That would
seem to indicate that there are, in fact, infinite possible bugs.
In particular interpreting random bits of memory as programming
instructions pretty much allows just about anything to happen. Now the
"demons fly out of your nose" is rhetorical hyperbole, but the main
point is that a lot of unintended consequences can follow from executing
random memory bits.
In fact, the latter is one of the standard methods used by crackers to
break into and comprise computer systems: Finding a way to write their
own code into a portion of memory that will then get executed.
No, this is one of the classic cases.
How would one go about making DELETE be destructive in the following
example:
(defvar *list* (list 1 1 1))
(delete 1 *list*) => NIL
*list* will not be NIL.
There is nothing you can do to the list (1 1 1) that will turn it into
NIL.
> On Dec 3, 1:08 am, m...@distorted.org.uk (Mark Wooding) wrote:
>> TheFlyingDutchman <zzbba...@aol.com> writes:
>> > I have been consistent in talking about what the function returns -
>>
>> And what it returns is an invalid address.
>
> Is it invalid at the time of the execution of the return statement?
>
>>
>> > We aaren't talking about a source code file. We are talking about an
>> > executable file,
>>
>> We aren't? My mistake. Where was that mentioned?
>
> Pascal B. had a program run and we were discussing his run and his
> output.
>
>>
>> > that was compiled by a specific C implementation that compiles in
>> > specific ways.
>>
>> Then I'm sure that the specific implementation will have been named,
>> clearly and explicitly. Umm... where?
>>
>
> OK, we know it's gcc, but don't know which version of gcc. Perhaps
> Pascal B. can do a "gcc --version".
It doesn't really depend on the compiler, but more on the system.
Even if x was passed in a register, as soon as you take its address, any
C implementation will have either to allocate memory for it, or have a
pointer format to designate registers. For automatic variables, or
parameters, the memory is usually allocated on the stack, because it has
to be released automatically upion function exit (this is why they're
called automatic variables).
Therefore, in either case, when the function return, the pointer won't
be valid. In an uncontrolled C implementation (ie. 80% of them, there
are a few C interpreter providing a controled environment), the pointer
will just keep pointing to the same memory (or register). But this
memory or register will be recycled and reused for something else. This
can occur soon or later.
Most notably, the time at which free memory is reused is not
deterministic, it doesn't depend on the program, but on the system, in
the case an interrupt occurs after foo returns, but before bar printf is
called the first time, the free memory above the stack can be used by
the interrupt handler, and override the data pointed to by the invalid
pointer.
And of course, the program will keep using the stack and the registers
so any data can override it at anytime.
In a controlled implementations, the pointer could be marked invalid,
and its use would signal an error. Or not.
I wasn't speaking generally. The domain for my comment regarding
infinite bugs was the tiny program written, compiled and run by Pascal
B.
> TheFlyingDutchman <zzbb...@aol.com> writes:
>
>> On Dec 2, 10:20 .am, w...@stablecross.com (Bob Felts) wrote:
>> > TheFlyingDutchman <zzbba...@aol.com> wrote:
>> >
>> > If it does, it's by luck and not design. .Pascal is right; once the
In short, TheFlyingDutchman, you were wrong in asking for the address of
lisp objects. If there exist such an address, it's an implementation
details (see the bunch of #+), that you should not try to use and be
concerned with.
> In particular interpreting random bits of memory as programming
> instructions pretty much allows just about anything to happen. Now the
> "demons fly out of your nose" is rhetorical hyperbole, but the main
> point is that a lot of unintended consequences can follow from executing
> random memory bits.
But nobody knows for sure that it's impossible. The universe allows
non-local actions (eg. particules entanglement), therefore it might be
possible for a computer device to have dragons fly out of your nose,
even if improbable given the current technology.
> In fact, the latter is one of the standard methods used by crackers to
> break into and comprise computer systems: Finding a way to write their
> own code into a portion of memory that will then get executed.
--
I said "the issue was". Not "the issue is". I already thanked Paul
Rubin for his second explanation as to how the second printf could
printf could print a different value than the first.
You can't turn (1 1 1) into NIL, but the you can still compute that NIL
destructively, e.g., by cutting cons cells out of the tail of the list
and then returning the list its tail depending on whether the head of
the list is item.
CL-USER >
(defun xdelete (item list)
(cond
((endp list)
list)
(t
(setf (rest list) (xdelete item (rest list)))
(if (eql item (first list))
(rest list)
list))))
CL-USER > (defparameter *list* (list 1 1 1))
*LIST*
CL-USER 23 > (xdelete 1 *list*)
NIL
CL-USER 24 > *list*
(1)
Did you have any interrupt handlers in your code? That's all I am/was
referring to.
You are correct. I don't understand how Team C-code-answers-Lisp-
questions can claim that a C object that no longer exists is a good
example of a Lisp object (or if you must - object) not having a memory
address.
To the contrary, I was proven right in asking, by the outstanding
responses of Andrew Philpot and Rob Warnock. Many thanks to both of
them.
And that was done beautifully by Rob Warnock and Andrew Philpot.
> Note the asymmetry in the situation: the people responding understand this
> stuff and you don't. The idea was for them to transfer some of their
> understanding to you. Why are you arguing with them?
Someone attempted to answer a question regarding Lisp objects, with C
code. The only thing they could have demonstrated with the code was...
drumroll please... an address that did not refer to an object. They
did not demonstrate any C "object" that did not have an address. You
are not alone in proclaiming otherwise. But the incorrectness of the
example (in several aspects) is why I am daring to argue with "those
that understand this stuff". The using the logical fallacies of
"Appeal to Authority" or "Appeal to Popularity" is really not
appropriate for a computer science discussion.
Leaving aside the afterthought of CLOS, in classical terms Lisp is not
an object-oriented language, and speaking of Lisp "objects" is (while
often useful) necessarily imprecise. Lisp has values that are not
necessarily objects, and values certaily don't need addresses.
Example: consider the expression
(let ((a (+ x y))
(b (- x y)))
(+ a b))
if x=5 and y=3 we would say b has the value 2. Does b have an address?
Not necessarily. AFAIK the compiler is allowed to optimize (+ a b) into
(+ x x) and completely eliminate b under lexical scoping rules, so there
is no memory allocated for b and no code generated to compute its value.
Is it forbidden by the standard for delete to go out of its way to ruin
its argument? I mean, in your example, returning NIL, but still setting
*list* to (nil).
What about if y is known to be an integer via (the ...) declarations,
but the compiler determines it is unbound? :)
I think in safe code, you can't do that optimization since
the unbound-variable must be thrown in that case. But I'm only
hypothesizing since I just recently ran into such things in my codes.
I don't yet fully understand the delineation of safe and unsafe codes
in lisp yet.
-pete
> (let ((a (+ x y))
> (b (- x y)))
> (+ a b))
>
> AFAIK the compiler is allowed to optimize (+ a b) into (+ x x)
That's probably true if it can prove that X and Y are exact (rational or
complex-rational). I don't think it's true if X or Y is a float (does
your optimization capture the possible rounding errors correctly?) --
and it's certainly wrong if Y is a NaN or infinity, or a non-numeric
value.
-- [mdw]