different pointer sizes and representations

L

Luca Forlizzi

Hello,

it is often remarked that pointer to objects and pointer to functions
can have different representations and also different sizes. Are these
the only possible differences or does the standard allows also
pointers to distinct object types (or to distinct function types) to
differ in representation and size (and alignment requirement, I
suppose) ?

Luca Forlizzi
 
K

Keith Thompson

Luca Forlizzi said:
it is often remarked that pointer to objects and pointer to functions
can have different representations and also different sizes. Are these
the only possible differences or does the standard allows also
pointers to distinct object types (or to distinct function types) to
differ in representation and size (and alignment requirement, I
suppose) ?

void* and pointers to character types have the same representation
and alignment requirements.

All pointers to struct types have the same representation and
alignment requirements. (Note that this refers to the alignment
of pointer objects, not the alignment of struct objects.)

All pointers to union types have the same representation and
alignment requirements.

(I don't *think* pointer-to-struct and pointer-to-union types
are required to have the same representation as each other, but
I'm not certain.)

There might be some guarantee regarding pointers to integer types
that differ only in signedness, but I'm not sure about that.

There are no other restrictions (beyond those that apply to scalar
types in general).

In most real-world implementations you're likely to encounter,
all pointers have the same representation. In rare cases,
pointer-to-function types might differ from pointer-to-object
types; typically they'd be bigger. In other rare cases, void*
and pointer-to-character types might differ from other object
pointer types. But an implementation could legally make double*
twice the size of int*.
 
T

Tim Rentsch

Keith Thompson said:
void* and pointers to character types have the same representation
and alignment requirements.

All pointers to struct types have the same representation and
alignment requirements. (Note that this refers to the alignment
of pointer objects, not the alignment of struct objects.)

All pointers to union types have the same representation and
alignment requirements.

(I don't *think* pointer-to-struct and pointer-to-union types
are required to have the same representation as each other, but
I'm not certain.)

They are not so required.
There might be some guarantee regarding pointers to integer types
that differ only in signedness, but I'm not sure about that.

Again there are not.
There are no other restrictions (beyond those that apply to scalar
types in general).

Except, pointers to compatible types have the same representation
and alignment requirements, and also qualified and non-qualified
versions of pointers to compatible types.
 
N

Nobody

In most real-world implementations you're likely to encounter, all
pointers have the same representation. In rare cases, pointer-to-function
types might differ from pointer-to-object types; typically they'd be
bigger.

Or smaller. E.g. with DOS code, it was more common to have one code
segment and multiple data segments (so a function pointer was just an
offset but a data pointer was segment+offset) than the other way around.

Also, on single-chip microcontrollers, you can typically have data in RAM,
flash and/or EEPROM (with different code being required to access each
type), but code can only go in flash. Usually, you would tag the pointer
with a (non-standard) qualifier; occasionally you might use "unified"
pointers (with the type stored in the pointer), but this makes for bloated
code).
 
M

Morris Keesan

it is often remarked that pointer to objects and pointer to functions
can have different representations and also different sizes. Are these
the only possible differences or does the standard allows also
pointers to distinct object types (or to distinct function types) to
differ in representation and size (and alignment requirement, I
suppose) ?

Pointers to different object types can definitely differ from each other.
I've used systems where (char *)s differed in size and representation from
all other pointers.

I can't find anything in the standard which would prevent different
function pointer types from having different sizes, etc.
C99 6.3.2.3 paragraph 8 says, "A pointer to a function of one type may be
converted to a pointer to a function of another type and back again; the
result shall compare equal to the original pointer."
 
T

Tim Rentsch

Morris Keesan said:
Pointers to different object types can definitely differ from each other.
I've used systems where (char *)s differed in size and representation from
all other pointers.

I can't find anything in the standard which would prevent different
function pointer types from having different sizes, etc.
C99 6.3.2.3 paragraph 8 says, "A pointer to a function of one type may be
converted to a pointer to a function of another type and back again; the
result shall compare equal to the original pointer."

If two function types have the same return type, they
are both compatible with a function type having the
same return type but not specifying any parameter types
(eg, like 'int foo()'), and so pointers to these two types
must have the same size and representation, etc, since they
both must match the common pointer type (eg, (int (*)(int))
and (int (*)(float)) both must match (int (*)())).

Pointers to functions not having the same return type don't
have to be the same size etc.
 
M

Malcolm McLean

Say we've got a chip with the following architecture: a 256 byte RAM
and a 65536 byte ROM, machine instructions accessing RAM through 8 bit
offsets that differ from the machine instruction to access ROM. What
dies that imply for pointer representation?
 
E

Eric Sosman

If two function types have the same return type, they
are both compatible with a function type having the
same return type but not specifying any parameter types
(eg, like 'int foo()'), and so pointers to these two types
must have the same size and representation, etc, since they
both must match the common pointer type (eg, (int (*)(int))
and (int (*)(float)) both must match (int (*)())).

Like Morris Keesan, I can't find Standard language saying that
the function pointers you mention are "compatible" in the sense that
the Standard defines the word. "May be converted" and "compatible"
aren't the same notion as I understand them; can you offer citations
to clarify?

Incidentally, note that an `(int (*)())' pointer and an
`(int (*)(float))' pointer cannot call the same target function;
one or the other is necessarily incorrect for the target.
 
K

Keith Thompson

Malcolm McLean said:
Say we've got a chip with the following architecture: a 256 byte RAM
and a 65536 byte ROM, machine instructions accessing RAM through 8 bit
offsets that differ from the machine instruction to access ROM. What
dies that imply for pointer representation?

Assuming functions are in ROM, it implies that function pointers
must be big enough to distinguish between any two functions in
the program.

If the implementation permitted no more than 255 functions, a
function pointer could be implemented as an 8-bit index into a table.

As for object pointers, I presume objects (at least constant ones)
can be stored in either RAM or ROM, so again, an object pointer
has to be big enough to distinguish between any two objects in
the program. For example, a function could have a "const char*"
parameter that points to an object that could be either in RAM
or in ROM, so you can't get away with 8-bit RAM-only pointers.
Using 8 bits for char* and 16 or more for const char* would be a
clever trick, but I don't think it would be conforming.

Then again, an implementation for such a small system is likely to
have non-conforming extensions to deal with the small RAM.
 
S

Seebs

Say we've got a chip with the following architecture: a 256 byte RAM
and a 65536 byte ROM, machine instructions accessing RAM through 8 bit
offsets that differ from the machine instruction to access ROM. What
dies that imply for pointer representation?

That it is almost certainly non-conforming.

-s
 
R

robertwessel2

Or smaller. E.g. with DOS code, it was more common to have one code
segment and multiple data segments (so a function pointer was just an
offset but a data pointer was segment+offset) than the other way around.


Not that it really matters anymore, but medium model (multiple code
segments, single data segment) was rather more commonly used than
compact model (other way 'round).

The reason was/is that multiple code segments were relatively easy and
low cost to deal with (assuming a single function does not need to
cross segment boundaries - and I never saw a compiler that allowed
that), you just need to replace the near calls and returns with far
ones, and adjust some offsets in the stack frames, which is basically
trivial. Using multiple data segments needed continuous management of
the segment registers, and usually brought a significant performance
penalty.

Most applications that needed multiple data segments ended up with
either a hybrid model (mainly small or medium model, but with some
"far" data, which required that that you explicitly mark some data
pointers in your code "far," and then needed to take some care passing
and converting pointers around), or as large model applications, where
multiple code *and* data segments are allowed. 64KB of code was often
restrictive anyway, so compact model just didn't match many
requirements.
 
T

Tim Rentsch

Eric Sosman said:
If two function types have the same return type, they
are both compatible with a function type having the
same return type but not specifying any parameter types
(eg, like 'int foo()'), and so pointers to these two types
must have the same size and representation, etc, since they
both must match the common pointer type (eg, (int (*)(int))
and (int (*)(float)) both must match (int (*)())).

[I am inverting the order of the response paragraphs.]

Incidentally, note that an `(int (*)())' pointer and an
`(int (*)(float))' pointer cannot call the same target function;
one or the other is necessarily incorrect for the target.

Sorry, you are absolutely right, it was a bad choice of example on
my part -- it should have been (int (*)(int)) and (int (*)(double)),
both being compatible with (int (*)()). My statement about which
pointer-to-function types have the same representation should be
modified accordingly, namely, pointers to functions that share a
common compatible type are the ones that must have the same size,
representation and alignment alignment requirements; more
specifically, just having the same return type isn't enough - the
parameter types must also be "suitable", which eliminates the pair
of (int (*)(float)) and essentially any other type.

Note that (int (*)(int)) and (int (*)(double)) are each compatible
with (int (*)()), by 6.7.5.3p15.

Like Morris Keesan, I can't find Standard language saying that
the function pointers you mention are "compatible" in the sense that
the Standard defines the word. "May be converted" and "compatible"
aren't the same notion as I understand them; can you offer citations
to clarify?

The (revised) pointer types are in fact not compatible, but what's
relevant is that the function types they are pointing to share a
common compatible type, because of 6.2.5p27, which reads in part:

Similarly, pointers to qualified or unqualified versions of
compatible types shall have the same representation and
alignment requirements.

Since each of the two types has the same representation etc as their
shared compatible type, each must have the same representation etc
as the other.

The compatibility (which again isn't relevant, but...) of each of
the function pointer types to the shared compatible type (int (*)())
follows from 6.7.5.1p2, which talks about pointer types generally:

For two pointer types to be compatible, both shall be
identically qualified and both shall be pointers to
compatible types.

That a little better now?
 
P

Peter Nilsson

Tim Rentsch said:
If two function types have the same return type, they
are both compatible with a function type having the
same return type but not specifying any parameter types
(eg, like 'int foo()'), ...

That's not true in the case of variadic functions. For
instance, int (*)(int, ...) is not compatible with int (*)().
 
T

Tim Rentsch

Peter Nilsson said:
That's not true in the case of variadic functions. For
instance, int (*)(int, ...) is not compatible with int (*)().

Right, and I should have mentioned that. It is possible
for two function types that are not compatible to be
"similar" in that they require pointers to such types
to have the same representation etc, but only if
neither or both have ellipses.
 
L

Luca Forlizzi

Right, and I should have mentioned that.  It is possible
for two function types that are not compatible to be
"similar" in that they require pointers to such types
to have the same representation etc, but only if
neither or both have ellipses.

If neither have ellipsis, then it is possibile that the two function
types are both compatible with a "common" function type, which is not
in prototype form. Then 6.2.5p27 applies, as you clearly explained in
your reply to Eric Sosman.
But if both have ellipsis, and are not compatible, I don't think there
is a "common ancestor" in the compatibility relation. In which cases,
and in the standard it is said that, pointers to such function types
have the same represetation etc... ?
 
L

Luca Forlizzi

If neither have ellipsis, then it is possibile that the two function
types are both compatible with a "common" function type, which is not
in prototype form. Then 6.2.5p27 applies, as you clearly explained in
your reply to Eric Sosman.
But if both have ellipsis, and are not compatible, I don't think there
is a "common ancestor" in the compatibility relation. In which cases,
and in the standard it is said that, pointers to such function types
have the same represetation etc... ?

I answer to myself, of course Tim is right, cases of pairs of function
types not compatible with each other but both compatible with a third
type exist also when both function types have ellipsis.

As an exercise, let C be the (binary) relation of compatibility
between types. Let D be the binary relation between pairs (T1,T2) of
types such that T1 and T2 are not compatibile, but type T0 exists that
is compatible with both T1 and T2. (i.e. D = C^2 - C). So given
(T1,T2) in D, another pair of types in D can be constructed as
function types returning, respectively T1 and T2 and with identical
parameters, or having T1 and T2 as corresponding parameters. And this
function types can both have parameter type lists ending with
ellipsis.

For instance int [4] and int [5] form a pair in D, so as int (*)[4]
and int (*)[5]. (incidentally, int (*)[4] and int (*)[5] are pointer
to object types not compatible with each other but both required to
have the same represetation and alignment requirments as int (*)[] and
as int (*)[n] ).
Then void (*pg1)( int, int (*)[4], ... ) and void (*pg2)( int, int (*)
[5], ... ) are pointer to function types not compatible with each
other but both required to have the same representation and alignment
requirments as void (*pg0)( int n, int (*)[n], ... ).

Similarly (considering the return types),
int ( * (*pf1)(int, ...) )[4] and int ( * (*pf2)(int, ...) )[5]
are pointer to function types not compatible with each other but both
required to have the same representation and alignment requirments as
int ( * (*)(int, ...) )[].
 

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,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top