Differences between pointers

J

Jason Curl

Hello,

Just a clarification of the specification about the C-Standard's
interpretation of pointers.

I know that 'char *' and 'void *' are equivalent and that 'int *' may be
different in representation to 'char *' (but most often there is no
difference on modern compilers/machines).

So if I define a function such as (e.g. often for callbacks)

void foo(void *data);

Then if I pass something that is a structure

struct testing {
int a;
char b;
};

int main(void)
{
struct testing x;
x.a = 1;
x.b = 2;

foo((void *)&x);
return 0;
}

is this concept valid? If not, what would be a valid way to achieve
portable results?

Best Regards,
Jason.
 
J

Jack Klein

Hello,

Just a clarification of the specification about the C-Standard's
interpretation of pointers.

I know that 'char *' and 'void *' are equivalent and that 'int *' may be
different in representation to 'char *' (but most often there is no
difference on modern compilers/machines).

So if I define a function such as (e.g. often for callbacks)

void foo(void *data);

Then if I pass something that is a structure

struct testing {
int a;
char b;
};

int main(void)
{
struct testing x;
x.a = 1;
x.b = 2;

foo((void *)&x);

The cast is unnecessary. The & operator takes the address of its
operand yielding a value of pointer to type, in this case pointer to
struct testing. In C, no cast is required to convert between pointer
to void and pointer to any other object type, not in either direction.

So rewrite this as:

foo(&x);
return 0;
}

is this concept valid? If not, what would be a valid way to achieve
portable results?

Best Regards,
Jason.

The concept is perfectly valid and portable as far as the code you
wrote. Actually using the pointer is another matter. It must be
converted to a pointer to object type before it can be used to
actually read or write values.

The only portable types it may be converted to are pointer to struct
testing, or pointer to int, in which case it will point to the 'a'
member of the testing structure.
 
P

Phenix Zhang

Jason said:
Hello,

Just a clarification of the specification about the C-Standard's
interpretation of pointers.

I know that 'char *' and 'void *' are equivalent and that 'int *' may be
different in representation to 'char *' (but most often there is no
difference on modern compilers/machines).

So if I define a function such as (e.g. often for callbacks)

void foo(void *data);

Then if I pass something that is a structure

struct testing {
int a;
char b;
};

int main(void)
{
struct testing x;
x.a = 1;
x.b = 2;

foo((void *)&x);
return 0;
}

is this concept valid? If not, what would be a valid way to achieve
portable results?

Best Regards,
Jason.
Why don't you write your code like this ?
void foo(struct testing *data)????
 
R

Richard Bos

Well... almost. They're required to have identical representation and
alignment, but only void * can be assigned to and from other object
pointers without casting.
The cast is unnecessary.
The concept is perfectly valid and portable as far as the code you
wrote. Actually using the pointer is another matter. It must be
converted to a pointer to object type before it can be used to
actually read or write values.

The only portable types it may be converted to are pointer to struct
testing, or pointer to int, in which case it will point to the 'a'
member of the testing structure.

And a pointer to unsigned char, in which case it may be used to inspect
the memory representation of the struct (which is implementation-
specific, and may (probably will) include padding bytes).

Richard
 
J

Jason Curl

Jack said:
The cast is unnecessary. The & operator takes the address of its
operand yielding a value of pointer to type, in this case pointer to
struct testing. In C, no cast is required to convert between pointer
to void and pointer to any other object type, not in either direction.

So rewrite this as:

foo(&x);




The concept is perfectly valid and portable as far as the code you
wrote. Actually using the pointer is another matter. It must be
converted to a pointer to object type before it can be used to
actually read or write values.

The only portable types it may be converted to are pointer to struct
testing, or pointer to int, in which case it will point to the 'a'
member of the testing structure.
Jack - because void* and int* can have a different internal
representation, can the size of the pointers change also? Or does the
C-spec state the size of the ptrs are the same?

Can there be an esoteric machine (or even just a compiler) built that
sizeof(void*)==4 and sizeof(int*)==6, so when the type cast is done,
data is lost on the function call resulting in an invalid pointer when
it's typecasted back from void* to int*?

If this is really the case, would the solution that I use the signature:

void foo(int*);

and then I can have

struct testing1 {
int a;
char b;
};

struct testing2 {
int a;
char *str;
}

and pass pointers to the structures typecasted to (int*)?

I find this question important for implementing, say, generic message
queues on embedded systems (where it's easy to have the first member of
the struct an int, that defines the message, and the remaining data in
the struct being the message).

Thanks again.
Jason.
 
P

pete

Phenix said:
Why don't you write your code like this ?
void foo(struct testing *data)????

That's what I think. It's not a generic function,
meant to work with pointers to various different types of data.
 
K

Keith Thompson

Jason Curl said:
Jack - because void* and int* can have a different internal
representation, can the size of the pointers change also? Or does the
C-spec state the size of the ptrs are the same?

I'm not Jack, but ...

There's no requirement that void* and int* have to be the same type.
Can there be an esoteric machine (or even just a compiler) built that
sizeof(void*)==4 and sizeof(int*)==6, so when the type cast is done,
data is lost on the function call resulting in an invalid pointer when
it's typecasted back from void* to int*?

I believe that conversion from int* to void* and back to int* is
guaranteed to yield a value equal to the original value. (If that
weren't the case, void* wouldn't be usable as a generic pointer type.)

If int* and void* are of different sizes, it's more likely that void*
is bigger. More information may be needed to point to any arbitrary
byte than to point to an integer. For example, a raw machine address
might point to a word, with a void* or char* consisting of a word
pointer plus an offset.

(I've never seen an implementation where int* and void* are of
different sizes; I've used systems with native word poitenrs, but the
offset for a void* or char* was stored in the unused high-order bits.)
 
F

Flash Gordon

Jason Curl wrote:

Jack - because void* and int* can have a different internal
representation, can the size of the pointers change also? Or does the
C-spec state the size of the ptrs are the same?

I'm not aware of anything that says the pointer sizes have to be the
same, but there are other guarantees that help you here...
Can there be an esoteric machine (or even just a compiler) built that
sizeof(void*)==4 and sizeof(int*)==6, so when the type cast is done,
data is lost on the function call resulting in an invalid pointer when
it's typecasted back from void* to int*?

The standard guarantees that you can assign any pointer to an object to
a pointer to void and then back and end up with a pointer to the
original type. I.e this will always work:

T foo;
T *fooptr = &foo;
void *vptr = fooptr;
T *foo2ptr = vptr;
if (fooptr == foo2ptr)
puts("Executed on all conforming implementations");

Note that the above is not guaranteed for function pointers.
If this is really the case, would the solution that I use the signature:

void foo(int*);

and then I can have

struct testing1 {
int a;
char b;
};

struct testing2 {
int a;
char *str;
}

and pass pointers to the structures typecasted to (int*)?

I find this question important for implementing, say, generic message
queues on embedded systems (where it's easy to have the first member of
the struct an int, that defines the message, and the remaining data in
the struct being the message).

Fortunately for you, there is another guarantee that further helps you
here. With two struct definitions that share a common initial set of
fields, i.e. the "int a" in your example above, those initial fields are
guaranteed to have the same layout. I believe you are also guaranteed to
have no padding before the *first* field.

I may not be Jack, but I'm sure someone will correct me if I've made a
mistake.
 
C

CBFalconer

Jason said:
Just a clarification of the specification about the C-Standard's
interpretation of pointers.

I know that 'char *' and 'void *' are equivalent and that 'int *'
may be different in representation to 'char *' (but most often
there is no difference on modern compilers/machines).

So if I define a function such as (e.g. often for callbacks)

void foo(void *data);

Then if I pass something that is a structure

struct testing {
int a;
char b;
};

int main(void)
{
struct testing x;
x.a = 1;
x.b = 2;

foo((void *)&x);
return 0;
}

is this concept valid? If not, what would be a valid way to achieve
portable results?

It's fine. However the cast is unnecessary and can prevent the
compiler from flagging errors, so remove it. Then the foo
implementation should include:

void foo(void *data)
{
struct testing *dp = data;

/* code using dp, and not data */
}

and you will note there is no sign of a cast anywhere. All this
allows that void* pointer to pass through various levels of
software that don't care exactly what it points to, with only the
originator and destination actually caring (or knowing).

Removing the cast from the caller means that if you ever alter foo
to specify the parameter type more closely (i.e. as a non-void*)
you will get a compiler error. With the caller cast the error will
be ignored.
 
R

Richard Bos

Keith Thompson said:
I'm not Jack, but ...

There's no requirement that void* and int* have to be the same type.

Erm.

void * and int * _are_ not the same type. One is a pointer to void, the
other a pointer to int.

YM "the same size", I presume.
I believe that conversion from int* to void* and back to int* is
guaranteed to yield a value equal to the original value.

'tis. Ditto for any other object pointer type: to void *, and back to
the _original_ type, must always work correctly. To void *, and "back"
to another type, need not work at all.
If int* and void* are of different sizes, it's more likely that void*
is bigger.

More likely, but not guaranteed; but even when int * is larger, the
transfer through void * must still work. This means that a larger int *
must contain padding bits, but that's allowed.

Richard
 
K

Keith Thompson

Erm.

void * and int * _are_ not the same type. One is a pointer to void, the
other a pointer to int.

YM "the same size", I presume.

D'oh! Yes, I meant the same size.

[...]
More likely, but not guaranteed; but even when int * is larger, the
transfer through void * must still work. This means that a larger int *
must contain padding bits, but that's allowed.

[Low-level nitpicking follows. Stop reading now if you value your
sanity.]

Basically correct, but strictly speaking the term "padding bits"
applies only to integer types. Also, it refers to a disinct subset of
the bits of a type representation, bits that don't contribute to the
value, but that doesn't necessarily apply in this case.

One off-the-wall example is that void* might be represented as a pure
binary machine address, and int* might be represented in biquinary
(7 bits per decimal digit). All the bits contribute to the value, so
there are no padding bits; it's just an inefficient representation.

Or if you prefer a more binary representation, you can double the size
with a mapping like:

00 --> 0001
01 --> 0010
10 --> 0100
11 --> 1000

This is, of course, totally unrealistic, though I suspect some
versions of the mythical DS9K use this representation (or something
even worse).

The standard talks about padding bits for integer types because it
places strong constraints on how integers are represented. There are
virtually no constraints on pointer representations (other than that
they're made of bits and bytes and can, like any type, be viewed as
arrays of unsigned char).
 
P

Peter Nilsson

Keith said:
More likely, but not guaranteed; but even when int * is larger, the
transfer through void * must still work. This means that a larger int *
must contain padding bits, but that's allowed.

[Low-level nitpicking follows. Stop reading now if you value your
sanity.]

Basically correct, but strictly speaking the term "padding bits"
applies only to integer types.

Since we're nitpicking ;), it's not as if the committee _invented_ the
term 'padding bits'. The term is not in italics within C99. Indeed, the
term 'padding' is used exclusively as a qualification without explicit
definition.
 
P

pete

Peter said:
Keith said:
If int* and void* are of different sizes,
it's more likely that void*
is bigger.

More likely, but not guaranteed;
but even when int * is larger, the
transfer through void * must still work.
This means that a larger int *
must contain padding bits, but that's allowed.

[Low-level nitpicking follows. Stop reading now if you value your
sanity.]

Basically correct, but strictly speaking the term "padding bits"
applies only to integer types.

Since we're nitpicking ;), it's not as if the committee _invented_ the
term 'padding bits'.
The term is not in italics within C99. Indeed, the
term 'padding' is used exclusively as a qualification without explicit
definition.

C does not allow bitwise operations
on any other types besides integer types.
No other types besides integer types, are described in
the standard as being decomposable into various kinds of bits.
Whether or not your pointer has a sign bit, is off topic
with regard to portable C programming.
 

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
474,161
Messages
2,570,892
Members
47,431
Latest member
ElyseG3173

Latest Threads

Top