简体   繁体   中英

Swap pointers addresses in C

I need to do an Ultra High Performance swap of two pointers in C (the addresses, not what they point to). I know that the addresses are aligned (multiple of 16) and consecutive and I've already checked that this preserves in every execution. The code is as follows:

Nodo a __attribute__((aligned(8))), b __attribute__((aligned(4)));
Nodo *nodo_superior __attribute__((aligned(4)));
nodo_superior = &a;
Nodo * nodo_actual __attribute__((aligned(4)));
nodo_actual=&b;
printf("%ld %ld\n", nodo_superior, nodo_actual);

and the result on console is like (please, note that the first address should be "over"-aligned in order the next method to work):

140594404335200 140594404335216

Now to swap the addresses, I want to change the bit 5 of each pointer:

nodo_superior ^= 16; 
nodo_actual ^= 16;

but, unfortunately the C compiler does not allow this because integers can not be mixed with pointer values: it's an error, not a warning. If I try to include the pointer in an union like this:

union {
    Nodo * nodo_actual;
    int local_int;
} na;

with the idea of doing an assigment like:

na.local_int ^= 16;

then the performance decreases because (I suppose) the compiler cannot apply/infer optimizations to nodo_actual if it is in an union. Any ideas to force the compiler to behave as I want? Of course, I could change the generated assembler but I do not consider it a good option,

If you want to toggle the bit 5 of each pointer you can write:

nodo_superior = (Nodo *)((uintptr_t)nodo_superior ^ 16);

uintptr_t is an integer type that (if exists) is guaranteed to be able to hold a pointer value without loss of information.

It's intended that this be done by the obvious isomorphism on a flat memory model, so it should work for you (although test it first to see).

Also, you should be using %p to printf a pointer, not %ld which causes undefined behaviour. To print uintptr_t see here

A swap is better performed "the old way", with a temporary variable.

Nodo* Swap= a; a= b; b= Swap;

This costs 2 reads and 2 writes.

The "smart" solution

a^= 16; b^= 16;

costs 2 reads, 2 xors and 2 writes.

I'm not sure I really understand what you mean by "swap two pointers". It seems you have a pair of nodes, and you want the compute the address of one node from the address of the other one.

A similar problem is when you have two numbers (for exemple 1 and 2 for player number) and want to change the player number from 1 to 2 and vice-versa. A usual trick is to compute

  opponent = (1+2) - player ;

You can play a similar trick with pointers, because some arithmetic is allowed :

int main(int argc, char **argv)
{
    struct Node x;
    struct Node y;

    printf("x is at %p,  y at %p\n", &x, &y);

    struct Node * one = &x;
    struct Node * other = &x + (&y - one); // (&x + &y) - one

    printf("if one is at %p,  the other is at %p\n", one, other);

    one = &y;
    other = &x + (&y - one);

    printf("if one is at %p,  the other is at %p\n", one, other);

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM