Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pointers assigned to pointers - confused

5 views
Skip to first unread message

Bela Lantos

unread,
Mar 9, 2001, 11:02:34 AM3/9/01
to

Hi,

In my C book there are examples of the legal assignments between different
types
of pointers. It is the following:

DECLARATIONS:
int *p;
float q;
void *v;

LEGAL ASSIGNMENTS:
p = 0;
p = (int *) 1;
p = v = q;
p = (int *) q;

I suppose there is an error in the DECLARATIONS, as the second line should
read:
float *q, because if q was not a pointer, ;

Now I wrote the following code to see the output:

#include <stdio.h>

int main(void)
{
int *p;
float i = 3.456, *q = &i;
void *v;
p = (int *) 1;
printf("%d\n", p);
p = (int *) q;
printf("%d\n", p);
v = q;
printf("%d\n", v);
return 0;
}

The compiler warns me on all of the printf() lines, "int format, pointer
arg (arg 2)"
The program prints the following:
1
-1073744124
-1073744124

What is (int *) doing? (int) here is the cast operator, but since 1 is an
integer, what is the point of using it in p = (int *) 1? What is the * doing
there? It is a dereferencing operator. Does the above mean that the value
stored
at the address where p points to must be integer, and will take the value
1?
This seems to be correct seeing the printout. Am I right?

If it is so, than what about p = (int *) q? I made q have the address of i,
so that I can get a value printed. The above takes the value stored at
the address q points to (which is i = 3.456), converts it from float to
integer,
and stores it at the address to which p points to.
Is it correct? so why than the strange printout?

If I accept that the book is correct, and write
float q = 3.456;
as well as
p = (int *) q;
the compiler complains, that "cannot convert to a pointer type".

If I change it to
p = (int) q;
the compiler prints, that "warning: assignment makes pointer from integer
without a cast".
Yet, the program will run, printing
1
3

I am a bit confused about all this.
What is happening here?
Can anyone help?

Bela


pete

unread,
Mar 9, 2001, 2:26:21 AM3/9/01
to

No. It takes the address of i, which is stored in q,
and copies it to p. If an int is 4 bytes long on your system,
then *p will be the integer value that corresponds to the contents
of the lower 4 bytes of i.

--
pete

Barry Schwarz

unread,
Mar 9, 2001, 2:36:54 AM3/9/01
to

The format specifier for a pointer is %p. You should also cast the
pointer to void* in the call.


<<Remove the del for email>>

TheSchof

unread,
Mar 9, 2001, 3:11:11 AM3/9/01
to
>What is (int *) doing? (int) here is the cast operator, but since 1 is an
>integer, what is the point of using it in p = (int *) 1?

So your compiler doesn't complain.

> What is the * doing
>there? It is a dereferencing operator. Does the above mean that the value
>stored
>at the address where p points to must be integer, and will take the value
>1?

p is a wild pointer, all the (int *) cast did was suppress a compiler warning,
but p now points to the memory location at address 1.
It does not mean that the value stored at the address where points to must be
integer. In fact, 1 could point to almost anything. Doesn't your book make
any mention of how dangerous this is?

>This seems to be correct seeing the printout. Am I right?

Yes, the first prints the address you assigned p to point to, in this case 1.
It's important to realize that you haven't dereferenced p so you aren't looking
at what it points to.
In the second you assign q to p. both are pointers, the cast is just to
suppress a compiler warning. Then you print the address of wherever i is.
v=q makes no difference to p or the data it points to, so you have the same on
the third.

>If it is so, than what about p = (int *) q? I made q have the address of i,
>so that I can get a value printed. The above takes the value stored at
>the address q points to (which is i = 3.456), converts it from float to
>integer,
>and stores it at the address to which p points to.
>Is it correct? so why than the strange printout?

In that program, the values are not converted, or stored at any other memory
location. All you are doing really is passing around the address of i. When a
pointer is created of any type, it does not set aside space for a value
automatically, and assigning one pointer to another won't copy the values
pointed to, it will just make them point to the same memory location.

best regards,
Alex

Chris Torek

unread,
Mar 9, 2001, 3:51:51 AM3/9/01
to
In article <3aa871c8$0$25479$7f31...@news01.syd.optusnet.com.au>

Bela Lantos <bela...@optusnet.com.au> writes:
>In my C book there are examples of the legal assignments between
>different types of pointers. It is the following:
>
>DECLARATIONS:
>int *p;
>float q;
>void *v;
>
>LEGAL ASSIGNMENTS:
>p = 0;
>p = (int *) 1;
>p = v = q;
>p = (int *) q;
>
>I suppose there is an error in the DECLARATIONS, as the second
>line should read:
>float *q ...

This is not the only error in the book, if it uses the words "legal"
and "illegal" in this manner. :-)

In particular, consider the line:

p = (int *)1;

This assignment is neither "legal" nor "illegal", because the C
standards do not use these terms. This assignment does, however,
have a very interesting effect on a Data General Eclipse: it sets
p to NULL!

In Standard C, an assignment either meets constraints, or fails to
meet constraints. Many people (and books) use the word "legal" to
mean "meets constraints", and "illegal" to mean "fails to meet
constraints and therefore requires a diagnostic". Unfortunately,
people (and books) *also* use the word "legal" to mean "is always
okay to do; no harm will come of this". (In fact, even in its more
general English usage, "legal" and "harmless" are quite different.
Consider the extreme, but legal, application of the death penalty.
This certainly harms the one to whom it is applied.)

>What is (int *) doing? (int) here is the cast operator, but since 1 is an
>integer, what is the point of using it in p = (int *) 1? What is the * doing
>there? It is a dereferencing operator.

A lone "*" is indeed an operator -- either multiply (a * b, binary
"*") or indirect (*p, unary "*") -- but "*" is not just for these
operators. After all, "/*" is the start of a comment, not a division
followed by an indirection "*" operator. Once you "internalize"
that each character has some arbitrary number meanings, this next
part should get easier:

(int *) is a cast to the type inside the parentheses. This type
is "pointer to int", aka "int *". What the cast is doing is applying
some implementation-defined transformation to the integer value 1.

The result of the cast has type "pointer to int" and some
hard-to-predict value. Because the result is "implementation-defined",
though, you can look at the documentation that comes with the
compiler. Somewhere in that documentation, there must be a
description of what happens here.

On the Data General machine I mentioned, what happens is that
the number is shifted right one bit. The result is all-zero-bits,
which the Data General happens to use for its internal NULL pointer.

On other machines, something else happens, and the result is
something different. In general, you should avoid this kind of
trickery. That saves you from having to look up the answers in
the implementation documents for every implementation you ever
plan to use.
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA, USA Domain: to...@bsdi.com +1 510 234 3167
http://claw.bsdi.com/torek/ (not always up) I report spam to abuse@.
Note: PacBell news service is rotten

Hallvard B Furuseth

unread,
Mar 9, 2001, 7:02:28 AM3/9/01
to
to...@elf.bsdi.com (Chris Torek) writes:

> p = (int *)1;
>

> This assignment does, however, have a very interesting effect on a
> Data General Eclipse: it sets p to NULL!

> (...)


> On the Data General machine I mentioned, what happens is that
> the number is shifted right one bit.

Just wondering: does it divide sizeof(int) - assuming that is 2 - so
(int*)1 == NULL but (void*)1 != NULL, or is there some other reason?

--
Hallvard

Chris Torek

unread,
Mar 9, 2001, 8:42:54 AM3/9/01
to
In article <98a5j7$28i$1...@elf.bsdi.com> I noted that:

>> p = (int *)1;
>> This assignment does, however, have a very interesting effect on a
>> Data General Eclipse: it sets p to NULL!
>> (...)
>> On the Data General machine I mentioned, what happens is that
>> the number is shifted right one bit.

In article <HBF.2001...@bombur.uio.no>


Hallvard B Furuseth <h.b.fu...@usit.uio.no> writes:
>Just wondering: does it divide sizeof(int) - assuming that is 2 - so
>(int*)1 == NULL but (void*)1 != NULL, or is there some other reason?

On that system, sizeof(int) was 4, so it is not sizeof(int) per se.
Yet you are correct, (void *)1 would not be equal to NULL.

I like to use the DG as an example because it is quite simple and
very similar to more familiar machines, yet also just different
enough to illustrate the reasons for C's pointer rules. There are
more exotic architectures (such as Lisp machines) where a C pointer
has nothing to do with a physical hardware address, but on the DG,
C's pointers, with the funny shifting that goes on, really *are*
hardware addresses.

Back in the late 1970s, Data General built a machine called the
"Nova" that was sort of a rival to DEC's PDP-11. The PDP-11 was
a byte-addressed machine, with 16-bit addresses, and hence limited
to 65536 bytes; but the Nova was a word-addressed machine, with
16-bit addresses. In theory, that should have given it a limit of
131072 bytes, but for some reason they decided to use the topmost
bit as an "indirect" bit, marking a memory cell as something to be
used only as a "pointer" rather than a "value". The hardware would
then automatically find the target of that pointer. (If you set
up a cell whose value was its own word address, but had the "I"
bit set, and then loaded from it, the machine would lock up, fetching
the same value over and over again in an endless hardware loop.)

Thus, a "word pointer" held the address of the word in memory, plus
an extra bit at the top for the "indirect" flag. There were 32768
possible words for such a pointer to point to.

For various reasons, byte-addressability was important enough to
include a separate "byte pointer" format. But if there were up to
32768 words, and each word contained two bytes, a "byte pointer"
would necessarily have to be a full 16-bits long. The "I" bit
would have to be discarded. The obvious way to handle this was to
shift the word pointer left, discarding the "I" bit and introducing
a zero-bit at the bottom, so as to point to byte zero of that word.

This "format" concept persisted into the 32-bit Eclipse as well.
(For much more background -- mostly social, not technical -- on
the development of the Eclipse, read the book "The Soul of a New
Machine".) So, like the Nova, the Eclipse had "byte pointers" and
"word pointers", and conversion between the two was accomplished
via shifting. (The "I" bit was still there as well, but now the
hardware had a limit on the number of indirects it would follow,
so that a multi-user OS would not lock up when one user did something
wrong. :-) )

When the developers went to write a C compiler, they were faced
with a choice. Every byte had to be addressable, of course, so
they had to offer byte pointers. The choice was limited to one
thing: either use byte pointers everywhere, and slow down *every*
use of "int *" and the like, or go ahead and use the machine's
native "word pointers" for speed, but at the cost of exposing the
fact that there were two kinds of pointers.

The C compiler on the machine I used did the latter. Whenever you
converted from "char *" to "int *", it shifted the pointer right;
whenever you converted from "int *" to "char *", it shifted the
pointer left. This machine and its compiler were out before there
was a 1989 ANSI C standard, but the standard's authors included
people who knew about the Data General, as well as even more exotic
architectures, and knew that, with care, Standard C could be made
to work well on such machines.

There is actually still an option here: conversion from (int *) to
(int) could well preserve the raw pointer value, and from (int) to
(int *) could again preserve the value. That is:

int i, *p;
...
i = (int)p;
...
p = (int *)i;

could let you get at the raw "word pointer". Alternatively, the
two conversions could "move through" the byte pointer format before
reaching "int". I am not in fact sure what the compilers do today.
If conversion from pointer to integer, and vice versa, always uses
a byte address, then (int *)1 is the same as (int *)0; but if not,
they might be different. The C compiler never used the "indirect"
bit, so shifting word pointers up to become byte pointers before
turning them into "int"s would never lose information, and I think
would be "less surprising".

Assuming I am right, then, we have:

((void *)1) produces a byte pointer with word address 0, byte 1;
((void *)2) produces a byte pointer with word address 1, byte 0;
((void *)3) produces a byte pointer with word address 1, byte 1;

((int *)1) produces a word pointer with word address 0 (== NULL);
((int *)2) produces a word pointer with word address 1; and
((int *)3) produces a word pointer with word address 1.

This means that for any arbitrary int "i", ((int *)(char *)i) is
the same as ((int *)i). The other option is for ((int *)1) to
produce a word pointer pointing to word 1, ((int *)2) to be word
2, and so on, but then ((int *)i) is different from -- numerically
half of -- ((int *)(char *)i).

Either implementation can be conforming, because the C language
makes such conversions implementation-defined.

Dik T. Winter

unread,
Mar 9, 2001, 8:50:59 AM3/9/01
to
In article <HBF.2001...@bombur.uio.no> Hallvard B Furuseth <h.b.fu...@usit.uio.no> writes:

sizeof(int) has nothing to do with it. The DG has two different pointers,
byte pointers and word pointers. The division by 2 is the conversion of
a byte pointer to a word pointer. But indeed, (void *)1 != NULL.
But if I remember that machine correctly, at least with some releases
(int *)1 would not be NULL either, as a NULL pointer would not have
all bits set to 0 (the upper bits should contain a ring number).
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/

Chris Torek

unread,
Mar 9, 2001, 9:44:57 AM3/9/01
to
In article <G9xns...@cwi.nl> Dik T. Winter <Dik.W...@cwi.nl> wrote:
>... The DG has two different pointers,

>byte pointers and word pointers. The division by 2 is the conversion of
>a byte pointer to a word pointer. But indeed, (void *)1 != NULL.
>But if I remember that machine correctly, at least with some releases
>(int *)1 would not be NULL either, as a NULL pointer would not have
>all bits set to 0 (the upper bits should contain a ring number).

On the system I used (however briefly), I am pretty sure that
all-zero-bits "worked" as NULL. I do not remember which ring
was which -- if ring 0 is user, that would fall out naturally.
Otherwise it must have been an extra compiler hack, or a speical
feature of the instruction set, to ignore ring bits when comparing
pointers.

Dik T. Winter

unread,
Mar 9, 2001, 10:41:36 AM3/9/01
to
In article <98aq99$3fv$1...@elf.bsdi.com> to...@elf.bsdi.com (Chris Torek) writes:
> In article <G9xns...@cwi.nl> Dik T. Winter <Dik.W...@cwi.nl> wrote:
> >... The DG has two different pointers,
> >byte pointers and word pointers. The division by 2 is the conversion of
> >a byte pointer to a word pointer. But indeed, (void *)1 != NULL.
> >But if I remember that machine correctly, at least with some releases
> >(int *)1 would not be NULL either, as a NULL pointer would not have
> >all bits set to 0 (the upper bits should contain a ring number).
>
> On the system I used (however briefly), I am pretty sure that
> all-zero-bits "worked" as NULL. I do not remember which ring
> was which -- if ring 0 is user, that would fall out naturally.
> Otherwise it must have been an extra compiler hack, or a speical
> feature of the instruction set, to ignore ring bits when comparing
> pointers.

If I remember right (but it is long ago), a lower ring number gave
more privileges, so the user ring would be ring 15. I have understood
from an article in this newsgroup (or was it news.lang.c?) that newer
compilers were indeed hacked to make all bits zero also NULL.

Hallvard B Furuseth

unread,
Mar 13, 2001, 3:07:46 AM3/13/01
to
Thanks.

--
Hallvard

0 new messages