I couldn't really describe my problem in the title, but basically I need some help with pointer arithmetic. Lets say you have a struct like following. Then you get a pointer to after the long in the struct in memory. Lets say the pointer is *p. so I would do p += sizeof(void *) to get p at the location of the next pointer. N开发者_如何学运维ow how do I actually make p point to what next points to rather than just have it point to where the next pointer is in memory?
struct freeblock {
long s;
// <--- THE POINTER WOULD BE POINTING HERE
struct freeblock *prev;
struct freeblock *next;
}
This has the potential to be a tricky question. You really don't want to be doing pointer arithmetic in your structs. The reason is Struct Alignment. What happens is if you have a struct like so:
struct a{
char x; //address 0x8
//potentially 3 bytes lost here:
int y; //address 0x12
}
y is not going to start one byte after x. To preserve alignment, so y starts on, say, an address that's a multiple of 4, there will be some padding bytes added after x.
If you have the address of a struct instance 0x08 and add the size of a char, yielding (0x09) you won't get the start of y 0x12, you'll get some garbage data that's part junk and part y.
In this case you should be OK as the same types are right after one another and you can do something like so:
freeblock* p = s.prev;
p += sizeof(p);
freeblock next = *p;
You really want to get a pointer to the struct and let the compiler compute the offsets for you.
Try this:
void *p; /* points to prev now */
struct freeblock *p1;
p = ((char *)p)+sizeof(struct freeblock *); /* advance p to next */
p1 = *(struct freeblock **)p; /* take whatever is in next */
p1 points to the same place as next.
Little explanation - pointers are typed in C. Adding 1 to a pointer means adding sizeof(type) to the actual address, so you need to bring the pointer to a proper type, or to char * if you want manual arithmetics, since sizeof(char) is 1.
Let's assume that you only know that the current value of p
, and that it points to a prev
field of a struct freeblock
instance. Meanwhile, remember that the "after the long" address may not be the same as the address of prev
field, e.g. if sizeof(long)==4
and pointers are aligned at 8 bytes (which is true for some 64-bit platforms); see Paul Rubel's answer for explanation.
Now you need to get the value of next
in the same instance, and dereference it. A standard-compliant solution should probably involve offsetof()
macro:
void * p;
...
size_t delta = offsetof(struct freeblock,next)-offsetof(struct freeblock,prev);
p = (void*) *(struct freeblock**)((char*)p+delta);
So you first calculate the difference between addresses of prev
and next
fields with help of offsetof()
, then cast p
to pointer-to-char in order to correctly add the delta and get the address of next
field, then cast it to pointer-to-pointer-to-freeblock, dereference that address to get the desired value, and finally assign the value to p
with casting to pointer-to-void (which can be omitted, but I would keep it in production code).
Pointer should point to a specific type variable, you cannot perform aritmethic actions on void*
.
But assuming it is some valid data type, then p++
or p += 1
will do the job, note that it is dangerous to start casting pointer to different types.
struct a{
long s;
int d;// <--- THE p POINTER WOULD BE POINTING HERE
struct freeblock *prev;
struct freeblock *next;
}
p += 1; // just like d += 1*sizeof(int);
struct a{
long s;
int d;
struct freeblock *prev; // <--- now p points here
struct freeblock *next;
}
You say that "p at the location of the next
pointer" and you want to "make p point to what next
points to":
I'm going to assume that p
is a char*
:
p = (char*) *((struct freeblock**) p);
A brief explanation:
Since p
is pointing at next
, p
is 'acting' as a pointer to a struct freeblock*
, or a struct freeblock**
. To get the value of what that struct freeblock**
is pointing to, you simply dereference it (resulting in another pointer, but of the wrong type). Cast that resulting pointer to the right type for p
and you're set.
Maybe you are trying to dereference p, like this:
char *p;
struct freeblock *x;
// you got the value for p here somehow.
x = *((struct freeblock **)p);
// now you can access the fields:
if (x->next != 0)
{ //...
}
Note that I have chosen to maintain my awareness that the type of x is different than the type of p. You could declare x and p both as void *, but you would have to cast x to '((struct freeblock *)x)' before you could get at the fields.
This is not guaranteed to work, due to compiler alignment issues, etc. Certainly a terrible way to design your code.
精彩评论