By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,695 Members | 1,338 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,695 IT Pros & Developers. It's quick & easy.

Some pointer quiestions again

P: n/a
According to Section A6.6 Pointers and Integers (k & R)

" A pointer to one type may be converted to a pointer to another type.
The resulting pointer may cause
addressing exceptions if the subject pointer does not refer to an
object suitably aligned in storage. It is
guaranteed that a pointer to an object may be converted to a pointer to
an object whose type requires less
or equally strict storage alignment and back again without change; the
notion of ``alignment'' is
implementation-dependent, but objects of the char types have least
strict alignment requirements. As
described in Par.A.6.8, a pointer may also be converted to type void *
and back again without change. "

It says that it is *guaranteed* that a pointer to an object may
be converted to an object whose type require equally stringent
storage alignment. My question is that since two pointer variables
may be of different size (as pointed out by lots of people
in this newsgroup ) how this could be possible ? It may be possible
that two pointer variables of different types have same
byte alignment restrictions but their sizes may be different. Under
these conditions how can we convert one to another ?

I would say that two pointer variables of different types
should never be allowed to convert from one type to other, because
same byte alignment does not mean same pointer variable size.

Secondly, section A.6.8 says that "previously char * pointers
played the role of generic (void) pointers"
Is this true presently also ?
Consider the following piece of code:
#include <stdlib.h>
int main(void)
{
unsigned char *c;
int *i;

i = malloc(sizeof(int));
c = i;
}

On comlilation, I get the warning,
cc: Warning: t.c, line 8: In this statement, the referenced type of the
pointer value "i" is "int", which is not compatible with "unsigned
char". (ptrmismatch)
c = i;
--------^

If char pointer can play the role of generic pointer, why I
am getting this warning ?

Nov 14 '05 #1
Share this Question
Share on Google+
16 Replies


P: n/a
ju**********@yahoo.co.in wrote:
According to Section A6.6 Pointers and Integers (k & R) " A pointer to one type may be converted to a pointer to another type.
The resulting pointer may cause
addressing exceptions if the subject pointer does not refer to an
object suitably aligned in storage. It is
guaranteed that a pointer to an object may be converted to a pointer to
an object whose type requires less
or equally strict storage alignment and back again without change; the
notion of ``alignment'' is
implementation-dependent, but objects of the char types have least
strict alignment requirements. As
described in Par.A.6.8, a pointer may also be converted to type void *
and back again without change. " It says that it is *guaranteed* that a pointer to an object may
be converted to an object whose type require equally stringent
storage alignment. My question is that since two pointer variables
may be of different size (as pointed out by lots of people
in this newsgroup ) how this could be possible ? It may be possible
that two pointer variables of different types have same
byte alignment restrictions but their sizes may be different. Under
these conditions how can we convert one to another ? I would say that two pointer variables of different types
should never be allowed to convert from one type to other, because
same byte alignment does not mean same pointer variable size.
If the sizes differ the compiler has to insert code (you should
but don't have to tell it by casting) to make the conversion work
out right. But it's only required to do so reasonably if the align-
ment restrictions of the type you convert to aren't stricter than
of the type you're converting from. A conversion of bit represen-
tations of pointers is not uncommon, e.g. a NULL pointer doesn't
have to have a binary representation where all bits are 0, but you
can compare a NULL pointer (e.g. returned by from malloc()) with
the value 0 anyway and the result must be true, even though the
NULL pointer when cast to an int (assuming equal sizes) wouldn't
result in the number 0 [1]. So the compiler has to insert code that
makes the result of this comparison come out correctly. And for a
legitimate conversion (i.e. one where the above rules about align-
ment restrictions are satisfied) between pointers of types which are
stored with different sizes the compiler also has to ensure that
this works correctly if it's a standard compliant C compiler [2].
Secondly, section A.6.8 says that "previously char * pointers
played the role of generic (void) pointers"
Is this true presently also ?
Consider the following piece of code:
#include <stdlib.h>
int main(void)
{
unsigned char *c;
int *i; i = malloc(sizeof(int));
c = i;
} On comlilation, I get the warning,
cc: Warning: t.c, line 8: In this statement, the referenced type of the
pointer value "i" is "int", which is not compatible with "unsigned
char". (ptrmismatch)
c = i;
--------^ If char pointer can play the role of generic pointer, why I
am getting this warning ?


It's a warning, not an error, telling you that you may be doing
something stupid. Not more and not less. The compiler will probably
assume that you know what you're doing when you would do the conver-
sion with an explicit cast (but it still could emit a warning, there
is no restriction in the standard what a compiler can complain about),
so it might let you get away with an explicit cast. But since an
assignment between different types is something potentially dangerous
the compiler here tries to help you. It's the same as getting a war-
ning for e.g.

if ( x = malloc( 100 * sizeof *x ) )

There's nothing inherently wrong with this, but most compilers will
still tell you that you may unintenionally be doing an assignment
instead of a comparison here since getting '=' and '==' mixed up
in an if-condition is such a common mistake.

Moreover, as you cite yourself, char pointers "previously" played
the role of generic pointers - that was before the C standard came
out, finally making the void pointer type mandatory and giving
it some magical qualities, i.e. requiring that all pointer types
can be converted to and from a void pointer (at the cost that
you can't dereference a void pointer). Actually, a char pointer
still has to have the same representation and alignment require-
ments as a void pointer, but since char pointers aren't a poor
mans "ersatz" for a true generic pointer anymore compilers tend
to point out also implicit conversions to a char pointer from a
different pointer type as suspicious.

Regards, Jens

[1] This is also the reason why forgetting to include <stdlib.h>
when you use malloc() and instead casting the return value
is not guaranteed to work. If <stdlib.h> isn't included the
compiler assumes that malloc() returns an int and thus mind-
lessly converts the return value of malloc() to an int (per-
haps even passing the value back to the caller via an data
only register - there are several architectures that have
different registers for data and addresses). Only then it does
the explicit conversion, now converting from an int to the
intended pointer type. But since a pointer is not necessarily
the same as its representation when interpreted as an int this
can result in "interesting" and probably unintended results,
e.g. a NULL pointer returned by malloc() becoming converted
to a non-NULL pointer on a machine where a NULL pointer is
not represented by all bits 0.

[2] Obviously, the implementor of a compiler for a machine where
different pointer types have different sizes has some problems
on his hands. One possible solution would by to use the size
required by the largest pointer type for storing all pointers,
even if that would cost some additional memory. Another one
would be to give up one writing a standard compliant C com-
piler like they did for DOS, where they invented new qualifiers
for pointers (i.e. far, huge etc.). Or perhaps on the target
machine there are some special tricks to solve the problem
(i.e. having a special bit stored with small sized pointers
that gets set when a pointer value from a larger sized pointer
gets stored in it and then indicates that it doesn't hold the
"real" address but just a pointer to another address where the
full-sized address can be found. And there are probably some
more clever ways I wouldn't know about, not being a C compiler
implementor.
--
\ Jens Thoms Toerring ___ Je***********@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Nov 14 '05 #2

P: n/a
Mac
On Tue, 31 May 2005 17:45:08 +0000, Jens.Toerring wrote:

[snip]
[...] requiring that all pointer types
can be converted to and from a void pointer [...]


Isn't this supposed to be all _object_ pointer types or something like
that? What I am getting at is that ISTR that you cannot store a function
pointer in a void pointer.

--Mac

Nov 14 '05 #3

P: n/a
Mac <fo*@bar.net> wrote:
On Tue, 31 May 2005 17:45:08 +0000, Jens.Toerring wrote: [snip]
[...] requiring that all pointer types
can be converted to and from a void pointer [...]

Isn't this supposed to be all _object_ pointer types or something like
that? What I am getting at is that ISTR that you cannot store a function
pointer in a void pointer.


You're right, at last as far as I understand the standard only
conversion between object type pointers and a void pointer is
guaranteed to work. Allowing to convert between void and func-
tion pointers is listed as a "Common extension", so it's not
portable.
Regards, Jens
--
\ Jens Thoms Toerring ___ Je***********@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Nov 14 '05 #4

P: n/a
Mac wrote:
On Tue, 31 May 2005 17:45:08 +0000, Jens.Toerring wrote:

[snip]
[...] requiring that all pointer types
can be converted to and from a void pointer [...]


Isn't this supposed to be all _object_ pointer types or something like
that? What I am getting at is that ISTR that you cannot store a function
pointer in a void pointer.


Correct. A function pointer can be converted to a void pointer [1] but
cannot portably be converted back again. A function pointer can be
converted to a function pointer of another type and back again though.

[1] I am not sure that a function pointer can even be converted to a
void pointer reliably, if not then there would appear to be no portable
way to print the value of a function pointer. If anyone can point to a
spot in the Standard that clarifies this, feel free.

Rob Gamble

Nov 14 '05 #5

P: n/a
Je***********@physik.fu-berlin.de wrote:
ju**********@yahoo.co.in wrote:
#include <stdlib.h>
int main(void)
{
unsigned char *c;
int *i;
c = i;
}

On comlilation, I get the warning,
cc: Warning: t.c, line 8: In this statement, the referenced
type of the pointer value "i" is "int", which is not
compatible with "unsigned char". (ptrmismatch)


It's a warning, not an error.


Actually it is a constraint violation with a diagnostic
required. I think this means that if the compiler goes on to
produce an executable anyway, then the behaviour is undefined.

Nov 14 '05 #6

P: n/a
Old Wolf <ol*****@inspire.net.nz> wrote:
Je***********@physik.fu-berlin.de wrote:
ju**********@yahoo.co.in wrote:
#include <stdlib.h>
int main(void)
{
unsigned char *c;
int *i;
c = i;
}
On comlilation, I get the warning,
cc: Warning: t.c, line 8: In this statement, the referenced
type of the pointer value "i" is "int", which is not
compatible with "unsigned char". (ptrmismatch)


It's a warning, not an error.

Actually it is a constraint violation with a diagnostic
required. I think this means that if the compiler goes on to
produce an executable anyway, then the behaviour is undefined.


Since a char pointer must follow the same alignment restrictions as
a void pointer (i.e. none that matter) shouldn't that assignment be
ok? I gueses I am too dense to see the constraint violation here. The
only constraint for the assignment operator I found is "An assignmen
operator shall have a modifiable lvalue as its lef operand." which is
guess isn't violated here. Can you clearify that for me?

Regards, Jens
--
\ Jens Thoms Toerring ___ Je***********@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Nov 14 '05 #7

P: n/a
Je***********@physik.fu-berlin.de wrote:

Old Wolf <ol*****@inspire.net.nz> wrote:
Je***********@physik.fu-berlin.de wrote:
ju**********@yahoo.co.in wrote:

#include <stdlib.h>
int main(void)
{
unsigned char *c;
int *i;
c = i;
}

On comlilation, I get the warning,
cc: Warning: t.c, line 8: In this statement, the referenced
type of the pointer value "i" is "int", which is not
compatible with "unsigned char". (ptrmismatch)

It's a warning, not an error.

Actually it is a constraint violation with a diagnostic
required. I think this means that if the compiler goes on to
produce an executable anyway, then the behaviour is undefined.


Since a char pointer must follow the same alignment restrictions as
a void pointer (i.e. none that matter) shouldn't that assignment be
ok? I gueses I am too dense to see the constraint violation here. The
only constraint for the assignment operator I found is "An assignmen
operator shall have a modifiable lvalue as its lef operand." which is
guess isn't violated here. Can you clearify that for me?


A cast is required.

N869
6.5.16.1 Simple assignment
Constraints
[#1] One of the following shall hold:
-- the left operand has qualified or unqualified
arithmetic type and the right has arithmetic type;
-- the left operand has a qualified or unqualified version
of a structure or union type compatible with the type
of the right;
-- both operands are pointers to qualified or unqualified
versions of compatible types, and the type pointed to
by the left has all the qualifiers of the type pointed
to by the right;
-- one operand is a pointer to an object or incomplete
type and the other is a pointer to a qualified or
unqualified version of void, and the type pointed to by
the left has all the qualifiers of the type pointed to
by the right; or
-- the left operand is a pointer and the right is a null
pointer constant.
-- the left operand has type _Bool and the right is a
pointer.

--
pete
Nov 14 '05 #8

P: n/a
ju**********@yahoo.co.in writes:
According to Section A6.6 Pointers and Integers (k & R)

" A pointer to one type may be converted to a pointer to another type.
The resulting pointer may cause
addressing exceptions if the subject pointer does not refer to an
object suitably aligned in storage. It is
guaranteed that a pointer to an object may be converted to a pointer to
an object whose type requires less
or equally strict storage alignment and back again without change; the
notion of ``alignment'' is
implementation-dependent, but objects of the char types have least
strict alignment requirements. As
described in Par.A.6.8, a pointer may also be converted to type void *
and back again without change. "

It says that it is *guaranteed* that a pointer to an object may
be converted to an object whose type require equally stringent
storage alignment. My question is that since two pointer variables
may be of different size (as pointed out by lots of people
in this newsgroup ) how this could be possible ? It may be possible
that two pointer variables of different types have same
byte alignment restrictions but their sizes may be different. Under
these conditions how can we convert one to another ?
Usually, you convert one pointer type to another by using a cast.

You were probably asking how the conversion can be done on the machine
level (i.e., what happens to the bits), and I told you instead how you
do it in a C program. But in fact, that's really all you need to
know. It's up to the compiler to figure out how to make the
conversion work properly, regardless of the relative sizes of the
types. It's like converting the int value 4 to type float and getting
4.0, or vice versa, even though there's no guarantee that they're the
same size.

In most implementations I'm familiar with, all object pointers happen
to be the same size. For an implementation on which different object
points have different sizes, I would expect that a pointer to a type
with the least strict alignment requirements would probably be bigger
than a pointer to a type with stricter alignment requirements. For
example, if type int is word-aligned, an int* can be represented as a
word pointer, which doesn't need to specify a byte within the word; a
byte pointer (char* or void*) might need to be bigger because it has
to contain more information. If an implementation follows this kind
of scheme, the rules in the standard imply that you can convert a
smaller pointer to a larger pointer without losing information, which
is perfectly sensible.

An system where word pointers (or something like that) are bigger than
byte pointers would probably be quite exotic. Presumably there would
be good reasons for its quirks (unless it's the DS9000, which doesn't
need to justify anything). Either a C implementation would have to
jump through some hoops to make everything work as the standard
specifies, or there wouldn't be a conforming C implementation on the
system.
I would say that two pointer variables of different types
should never be allowed to convert from one type to other, because
same byte alignment does not mean same pointer variable size.
Differing pointer sizes shouldn't be a problem.
Secondly, section A.6.8 says that "previously char * pointers
played the role of generic (void) pointers"
Is this true presently also ?
Consider the following piece of code:
#include <stdlib.h>
int main(void)
{
unsigned char *c;
int *i;

i = malloc(sizeof(int));
c = i;
}

On comlilation, I get the warning,
cc: Warning: t.c, line 8: In this statement, the referenced type of the
pointer value "i" is "int", which is not compatible with "unsigned
char". (ptrmismatch)
c = i;
--------^

If char pointer can play the role of generic pointer, why I
am getting this warning ?


You're getting a diagnostic because the types unsigned char* and int*
are not assignment compatible. char* pointers can still be used as
generic pointers, but the language doesn't go out of its way to make
it easy to do so. In particular, there are implicit conversions
between void* and any object pointer type, but not between char* and
any object pointer type.

If you used an explicit cast:

unsigned char *c;
int *i;

i = malloc(sizeof *i);
c = (unsigned char *)i;

the compiler wouldn't complain, and c would point to the first byte of
the allocated block of memory (or would be a null pointer if the
malloc failed).

A couple of tangential style points:

I changed "sizeof(int)" to "sizeof *i", for reasons that have been
discussed here just recently.

The name "i" is typically used for an integer variable used as an
index. Using it as the name of a pointer will lead to confusion.
Likewise for "c", thought this isn't quite as bad. It's not a big
deal in a tiny sample program like yours, but it's easier to stick to
good naming habits if you use them consistently.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #9

P: n/a
pete <pf*****@mindspring.com> wrote:
Je***********@physik.fu-berlin.de wrote:

Old Wolf <ol*****@inspire.net.nz> wrote:
> Je***********@physik.fu-berlin.de wrote:
>> ju**********@yahoo.co.in wrote:
>>
>>> #include <stdlib.h>
>>> int main(void)
>>> {
>>> unsigned char *c;
>>> int *i;
>>> c = i;
>>> }
>>
>>> On comlilation, I get the warning,
>>> cc: Warning: t.c, line 8: In this statement, the referenced
>>> type of the pointer value "i" is "int", which is not
>>> compatible with "unsigned char". (ptrmismatch)
>>
>> It's a warning, not an error.
> Actually it is a constraint violation with a diagnostic
> required. I think this means that if the compiler goes on to
> produce an executable anyway, then the behaviour is undefined.


Since a char pointer must follow the same alignment restrictions as
a void pointer (i.e. none that matter) shouldn't that assignment be
ok? I gueses I am too dense to see the constraint violation here. The
only constraint for the assignment operator I found is "An assignmen
operator shall have a modifiable lvalue as its lef operand." which is
guess isn't violated here. Can you clearify that for me?

A cast is required. N869
6.5.16.1 Simple assignment
Constraints
[#1] One of the following shall hold:
-- the left operand has qualified or unqualified
arithmetic type and the right has arithmetic type;
-- the left operand has a qualified or unqualified version
of a structure or union type compatible with the type
of the right;
-- both operands are pointers to qualified or unqualified
versions of compatible types, and the type pointed to
by the left has all the qualifiers of the type pointed
to by the right;
-- one operand is a pointer to an object or incomplete
type and the other is a pointer to a qualified or
unqualified version of void, and the type pointed to by
the left has all the qualifiers of the type pointed to
by the right; or
-- the left operand is a pointer and the right is a null
pointer constant.
-- the left operand has type _Bool and the right is a
pointer.


Thank you. I should have scrolled down a bit further...

Regards, Jens
--
\ Jens Thoms Toerring ___ Je***********@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Nov 14 '05 #10

P: n/a
pete wrote:
Je***********@physik.fu-berlin.de wrote:
Old Wolf <ol*****@inspire.net.nz> wrote:
Je***********@physik.fu-berlin.de wrote:

ju**********@yahoo.co.in wrote:
>#include <stdlib.h>
>int main(void)
>{
> unsigned char *c;
> int *i;
> c = i;
>}

>On comlilation, I get the warning,
>cc: Warning: t.c, line 8: In this statement, the referenced
>type of the pointer value "i" is "int", which is not
>compatible with "unsigned char". (ptrmismatch)

It's a warning, not an error.

Actually it is a constraint violation with a diagnostic
required. I think this means that if the compiler goes on to
produce an executable anyway, then the behaviour is undefined.


Since a char pointer must follow the same alignment restrictions as
a void pointer (i.e. none that matter) shouldn't that assignment be
ok? I gueses I am too dense to see the constraint violation here. The
only constraint for the assignment operator I found is "An assignmen
operator shall have a modifiable lvalue as its lef operand." which is
guess isn't violated here. Can you clearify that for me?

A cast is required.

N869
6.5.16.1 Simple assignment
Constraints
[#1] One of the following shall hold:
-- the left operand has qualified or unqualified
arithmetic type and the right has arithmetic type;
-- the left operand has a qualified or unqualified version
of a structure or union type compatible with the type
of the right;
-- both operands are pointers to qualified or unqualified
versions of compatible types, and the type pointed to
by the left has all the qualifiers of the type pointed
to by the right;
-- one operand is a pointer to an object or incomplete
type and the other is a pointer to a qualified or
unqualified version of void, and the type pointed to by
the left has all the qualifiers of the type pointed to
by the right; or
-- the left operand is a pointer and the right is a null
pointer constant.
-- the left operand has type _Bool and the right is a
pointer.


Just jumping in.. Has anybody noticed that i has no assigned value? How
could 'c = i;' do anything definable?

--
Joe Wright mailto:jo********@comcast.net
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Nov 14 '05 #11

P: n/a
Joe Wright wrote:
pete wrote:
Je***********@physik.fu-berlin.de wrote:
Old Wolf <ol*****@inspire.net.nz> wrote:

Je***********@physik.fu-berlin.de wrote:

>ju**********@yahoo.co.in wrote:
>
>
>>#include <stdlib.h>
>>int main(void)
>>{
>> unsigned char *c;
>> int *i;
>> c = i;
>>}
>
>>On comlilation, I get the warning,
>>cc: Warning: t.c, line 8: In this statement, the referenced
>>type of the pointer value "i" is "int", which is not
>>compatible with "unsigned char". (ptrmismatch)
[snip]
Just jumping in.. Has anybody noticed that i has no assigned value? How
could 'c = i;' do anything definable?


i was assigned the return value of malloc in the original post, this
line was cut out when Old Wolf posted a followup.

Robert Gamble

Nov 14 '05 #12

P: n/a
In article <11**********************@o13g2000cwo.googlegroups .com>
Robert Gamble <rg*******@gmail.com> wrote:
Correct. A function pointer can be converted to a void pointer [1] but
cannot portably be converted back again. ...
You can do the conversion (in either direction) with a cast:

void f(void (*fp)(void)) {
void *p = (void *)fp;
...
}

but the result is not necessarily useful. For instance, older x86
implementations have models in which "char *"/"void *" is 32 bits
long, while "void (*)(void)" is only 16 bits; and more models in
which "char *"/"void *" is 16 bits long, but "void (*)(void)" is
32 bits. Clearly, in any system on which one conversion preserves
all the bits (by going from 16 bits to 32 bits), the opposite
conversion destroys some bits (by going from 32 to 16).

Since both systems exist, both conversions destroy some bits on
some systems. (That is, suppose System A has 16-bit function
pointers and 32-bit data pointers; and System B has 32-bit function
pointers and 16-bit data pointers. On System A, going from function
to data preserves everything, but going from data to function wipes
out bits. So the [1989] C standard could have attempted to mandate
that "function to data preserves bits" -- but then System B loses
out, because on System B, it is the function-to-data conversion
that loses bits, while the data-to-function conversion preserves
them.)
[1] I am not sure that a function pointer can even be converted to a
void pointer reliably, if not then there would appear to be no portable
way to print the value of a function pointer.


This is indeed the case.

I have seen a proposal for a "%P" (uppercase P) conversion to print
a function pointer, to solve this problem. Of course, the output
for "%p" (lowercase p) is already implementation-defined anyway, and
printing "void *" data pointers is thus of limited usefuless. There
seems to be no great clamor for printing function pointers.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #13

P: n/a
Chris Torek wrote:
In article <11**********************@o13g2000cwo.googlegroups .com>
Robert Gamble <rg*******@gmail.com> wrote:
Correct. A function pointer can be converted to a void pointer [1] but
cannot portably be converted back again. ...


You can do the conversion (in either direction) with a cast:

void f(void (*fp)(void)) {
void *p = (void *)fp;
...
}

but the result is not necessarily useful. For instance, older x86
implementations have models in which "char *"/"void *" is 32 bits
long, while "void (*)(void)" is only 16 bits; and more models in
which "char *"/"void *" is 16 bits long, but "void (*)(void)" is
32 bits. Clearly, in any system on which one conversion preserves
all the bits (by going from 16 bits to 32 bits), the opposite
conversion destroys some bits (by going from 32 to 16).

Since both systems exist, both conversions destroy some bits on
some systems. (That is, suppose System A has 16-bit function
pointers and 32-bit data pointers; and System B has 32-bit function
pointers and 16-bit data pointers. On System A, going from function
to data preserves everything, but going from data to function wipes
out bits. So the [1989] C standard could have attempted to mandate
that "function to data preserves bits" -- but then System B loses
out, because on System B, it is the function-to-data conversion
that loses bits, while the data-to-function conversion preserves
them.)
[1] I am not sure that a function pointer can even be converted to a
void pointer reliably, if not then there would appear to be no portable
way to print the value of a function pointer.


This is indeed the case.

I have seen a proposal for a "%P" (uppercase P) conversion to print
a function pointer, to solve this problem. Of course, the output
for "%p" (lowercase p) is already implementation-defined anyway, and
printing "void *" data pointers is thus of limited usefuless. There
seems to be no great clamor for printing function pointers.


Thank you very much for your detailed clarification. I had long
suspected that this was the case but hadn't given it too much thought.
Your explanation makes a lot of sense and clears this up for me.

Robert Gamble

Nov 14 '05 #14

P: n/a
In article <11**********************@g14g2000cwa.googlegroups .com>
<ju**********@yahoo.co.in> wrote:
According to Section A6.6 Pointers and Integers (K & R) ...
It is important to note that K&R is not the Standard, and if
what K&R says disagrees with what the Standard says, the Standard
"wins". :-)

That said, I think K&R2 A6.6 is correct.
It says that it is *guaranteed* that a pointer to an object may
be converted to an object whose type require equally stringent
storage alignment. My question is that since two pointer variables
may be of different size (as pointed out by lots of people
in this newsgroup ) how this could be possible ? It may be possible
that two pointer variables of different types have same
byte alignment restrictions but their sizes may be different. Under
these conditions how can we convert one to another ?
Suppose we have two types T1 and T2, and declare two pointers,
one to each type:

T1 *p1;
T2 *p2;

and give at least one of them a suitable value (e.g., result of
malloc(), or addresses of T1 and/or T2 typed objects).

The claim K&R make here is that if p2 has a valid value, the
code sequence:

p1 = (T2 *)p2;
p2 = NULL;
...
p2 = (T2 *)p1;

will leave p2 unchanged.

Let us imagine, now, that "T1 *" is 47 bits long, while "T2 *"
is nine million and three (9,000,003) bits long. Clearly, then,
the conversion from "T2 *" stored in p2 to "T1 *" stored in p1
discards just under nine million (9000003-47 = 8999956) bits.

What K&R2 A6.6 claims, then, is that one of two things must
hold: either those 8999956 bits are "unimportant" (and can be
regenerated by the conversion that restores p2), or else the
alignment of a "T1" must be more strict than that of a "T2".

On more-typical machines (e.g., MIPS, Alpha) that have alignment
constraints, or older machines with multiple pointer formats, we
find that these are all true. Indeed, the very reason that the
older machines have multiple pointer formats is because
less-strictly-aligned types -- usually "char *" -- need more bits,
to be able to represent "sub-word" values. That is, the machine
has a native "word pointer" type (perhaps 32 or 36 bits long) that
gives the address of a 32-bit word, or 36-bit word, or 18-bit word
(depending on the machine). All 32 or 36 bits are used. The C
compiler on that machine, however, makes "char *" either 8 or 9
bits long (9 bits for the 18- or 36-bit word machine), and needs
to know not only which machine *word* contains the desired "C byte",
but also which of the two or four "sub-bytes" within that word
should be chosen. This needs one or two extra bits.

Hence, we find that on the ancient PR1ME, sizeof(int *) is 4
(four 8-bit bytes = 32 bits) but sizeof(char *) is 6 (48 bits).
The "int *" type has more-strict alignment and can only point
to machine words. Given an arbitrary "char *" value:

char buf[N];
int i = <some expression>;
char *p = &buf[i];

if we convert "p" to "int *", we discard the byte-offset stored
in the extra bits:

int *ip = (int *)p;
char *p2 = (char *)ip;
printf("p - p2 = %d\n", (int)(p - p2));

Depending on the value of "i", the result of the printf is 0, 1,
2, or 3. The byte offset (which was 0, 1, 2, or 3) was discarded
when we computed a value to store in "ip", and when we converted
"ip" to put into "p2", a new byte offset (of 0) was created. The
expression (p - p2) computes the byte offset that was discarded.
I would say that two pointer variables of different types
should never be allowed to convert from one type to other, because
same byte alignment does not mean same pointer variable size.
This is certainly *safer* than the assertion in K&R.
Secondly, section A.6.8 says that "previously char * pointers
played the role of generic (void) pointers"
Is this true presently also ?


It is no longer true, because now "void *" plays the role of "void *".

In "K&R C" (pre-1989-Standard), it *was* true, but the special
dispensation of "cast-free conversion when mixing `void *' with
other data pointers" did not exist. Thus, in K&R C, one really
did have to write, e.g.:

int *ip = (int *)malloc(N * sizeof *ip);

The need for the cast went away in the 1989 C standard, which
added "void *" (and changed the type of malloc()).

In order to avoid breaking pre-1989 code, the 1989 standard required
that compilers use the same "internal bits" for "char *" and "void *".
The 1999 standard continues this requirement, even though a decade
has passed since it was really necessary. Note that the types --
"char *" and "void *" -- are different, even though the machine-level
representation is required to be the same. This is similar to the
way plain "char" is identical, "under the hood", to either signed
char or unsigned char -- CHAR_MAX is equal to either UCHAR_MAX or
SCHAR_MAX, and CHAR_MIN is equal to either 0 or SCHAR_MIN -- yet
the types remain different:

#include <limits.h>

#if CHAR_MAX == UCHAR_MAX
#define PLAIN_SAME_AS unsigned char
#else
#define PLAIN_SAME_AS signed char
#endif

void f(void) {
char *cp;
PLAIN_SAME_AS *pp;
...
pp = cp; /* a diagnostic is required here */
...
}
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #15

P: n/a
Chris Torek <no****@torek.net> writes:
[...]
I have seen a proposal for a "%P" (uppercase P) conversion to print
a function pointer, to solve this problem. Of course, the output
for "%p" (lowercase p) is already implementation-defined anyway, and
printing "void *" data pointers is thus of limited usefuless. There
seems to be no great clamor for printing function pointers.


If you really want to print (a textual representation of) the value of
a function pointer, you can either cast it to void* and print it using
"%p" (and live with the fact that it's blatantly non-portable), or
treat the pointer value as an array of unsigned char and print it in,
say, hexadecimal. The latter technique should work for any type,
though the relationship between the output and the actual value is
largely system-specific.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #16

P: n/a
Chris Torek <no****@torek.net> writes:
[...]
Let us imagine, now, that "T1 *" is 47 bits long, while "T2 *"
is nine million and three (9,000,003) bits long.

[...]

Surely you mean nine million and thirty (9,000,030) bits.

If "T1 *" is 47 bits long, then CHAR_BIT must be 47 (it can't be
smaller because 47 is prime), and the size in bits of "T2 *" (or of
any type) must be an integer multiple of CHAR_BIT.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #17

This discussion thread is closed

Replies have been disabled for this discussion.