Pointers assigned to pointers - confused
Author |
Message |
Bela Lanto #1 / 11
|
 Pointers assigned to pointers - confused
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; Quote: }
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
|
Wed, 27 Aug 2003 00:02:34 GMT |
|
 |
pete #2 / 11
|
 Pointers assigned to pointers - confused
Quote:
> 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?
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
|
Tue, 26 Aug 2003 15:26:21 GMT |
|
 |
Barry Schwar #3 / 11
|
 Pointers assigned to pointers - confused
On Fri, 9 Mar 2001 16:02:34 +0000, Bela Lantos Quote:
>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
The format specifier for a pointer is %p. You should also cast the pointer to void* in the call. <<Remove the del for email>>
|
Tue, 26 Aug 2003 15:36:54 GMT |
|
 |
TheSch #4 / 11
|
 Pointers assigned to pointers - confused
Quote: >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. Quote: > 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? Quote: >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. Quote: >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
|
Tue, 26 Aug 2003 16:11:11 GMT |
|
 |
Chris Tor #5 / 11
|
 Pointers assigned to pointers - confused
Quote: >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.) Quote: >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
Note: PacBell news service is rotten
|
Tue, 26 Aug 2003 16:51:51 GMT |
|
 |
Hallvard B Furuset #6 / 11
|
 Pointers assigned to pointers - confused
Quote:
> 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
|
Tue, 26 Aug 2003 20:02:28 GMT |
|
 |
Chris Tor #7 / 11
|
 Pointers assigned to pointers - confused
Quote: >> 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.
Quote: >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 {*filter*} 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 {*filter*} 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. -- In-Real-Life: Chris Torek, Berkeley Software Design Inc
Note: PacBell news service is rotten
|
Tue, 26 Aug 2003 21:42:54 GMT |
|
 |
Dik T. Winte #8 / 11
|
 Pointers assigned to pointers - confused
> > 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? 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/
|
Tue, 26 Aug 2003 21:50:59 GMT |
|
 |
Chris Tor #9 / 11
|
 Pointers assigned to pointers - confused
Quote: >... 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. -- In-Real-Life: Chris Torek, Berkeley Software Design Inc
Note: PacBell news service is rotten
|
Tue, 26 Aug 2003 22:44:57 GMT |
|
 |
Dik T. Winte #10 / 11
|
 Pointers assigned to pointers - confused
> >... 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. -- dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131 home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
|
Tue, 26 Aug 2003 23:41:36 GMT |
|
 |
Hallvard B Furuset #11 / 11
|
 Pointers assigned to pointers - confused
|
Sat, 30 Aug 2003 16:07:46 GMT |
|
|
|