The exact answer is implementation dependent and can be introspected
using the UPGRADED-ARRAY-ELEMENT-TYPE function.
However, in this case, there's a concensus:
$ clall -r '(upgraded-array-element-type (quote (or null fixnum)))'
Armed Bear Common Lisp --> T
Clozure Common Lisp --> T
CLISP --> T
ECL --> T
SBCL --> T
ie, indeed the array will contain references to the objects.
However, since fixnum are immutable, the implementation is allowed to
make copies of them any time, so it may avoid a pointer, and instead
store the value of the fixnum directly in the array slots. And
similarly, since NIL is such a common and important symbol, it is (or
was) often implemented as a special immediate value and therefore stored
without a pointer to the NIL symbol structure. (But probably in all
those modern implementations, it's actually just a pointer to the NIL
symbol structure, I've not checked them all).
Really, to learn more about it, you need to check the very low-level of
your implementation.
Notice for double-float it's the same:
$ clall -r '(upgraded-array-element-type (quote (or null double-float)))'
Armed Bear Common Lisp --> T
Clozure Common Lisp --> T
CLISP --> T
ECL --> T
SBCL --> T
In the case of fixnum, you could encode a "nil" value if you have one
unused fixnum (possibly most-negative-fixnum, since
(- most-negative-fixnum) will often not be a fixnum.
(it is not in all the above implementations, but notice that an
implementation may choose to make it a fixnum (ie. if it uses more than
16 bits, most-negative-fixnum can be (- most-positive-fixnum).
It could as well be (* (- most-positive-fixnum) 10)
or (truncate (- most-positive-fixnum) 10) or anything else, as long as
it's less than -2^15…))).
In the case of double-float, again, it will be implementation dependent,
but if your implementation uses IEEE floating point numbers, then you
could choose one NAN to represent a "nil", and thus be able to use a
specialized array too.
--
__Pascal J. Bourguignon
http://www.informatimago.com