Swap without worrying about the data type

402 views Asked by At

Is that implementation valid and safe to swap two array or variable without worrying about their data type? Or should I use a function pointer?.

This type of code is focusing about using a good implementation of void pointers to swap without worring about the data type.

#include <stdio.h>
#include <string.h>

void swap(void *, void *, int);

int main(void) {

  char a[] = "home";
  char b[] = "door";

  printf("%s %s\n", a, b);
  swap(&a, &b, sizeof(a));
  printf("%s %s \n", a, b);

  return 0;
}

void swap( void *a, void *b, int siz){
  char buff[siz]; // I voluntarily omitted dynamic allocation.
  memcpy(buff,a,siz);
  memcpy(a,b,siz);
  memcpy(b,buff,siz);
}
5

There are 5 answers

3
Mou On

I solved this by C, code for your reference.

Using uint16_t is neccessary to hold the carry bit when two large uint8_t variables plus.

int main(){
    uint8_t tmp[9] = {0x0};
    for (int i=9-1; i>=0 ; i--) *(tmp+i) = 0xff-i;
    uint16_t tmp2[9];
    for (int i=0; i<9; i++) tmp2[i] = tmp[i];
    byte_swap_uint8_data(tmp2, 9);
}

void byte_swap_uint8_data(uint16_t* data, int w) {
    if (w < 2) return;
    for (int i=0; i<w/2; i++) {
        data[i] += data[w-1-i];
        data[w-1-i] = data[i] - data[w-1-i];
        data[i] = data[i] - data[w-1-i];
     }
}
2
Taha Paksu On

You can just swap their addresses without further operation to swap two variables/arrays. I tried this and it works:

#include <stdio.h>

int main(void) {
    char *a = "home";
    char *b = "root";
    char *c = a, *d = b;
    printf("%s %s\n", a, b);
    a = d;
    b = c;
    printf("%s %s \n", a, b);
    return 0;
}

Outputs:

home root
root home 

https://ideone.com/MMCOpf

0
Toby Speight On

In C, this approach is usually okay (and it's used by standard functions such as qsort()). The contra-indications that indicate you can't use this are when there are any pointers to your objects or their members. Also, take care in multi-threaded code.

Note that in C++, we have std::swap() which will respect user-defined copy/move constructors; copying a C++ object by simply copying its memory is not (in general) valid.

0
machine_1 On

It is safe to use generic pointers to swap, however you must make sure you get the sizes right, and that you don't overflow any array or object:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int swap(void *a, void *b,
    size_t a_size, size_t b_size);

int main(void) {

    char a[] = "home";
    char b[] = "door";

    printf("%s %s\n", a, b);

    int ret = swap(a, b, sizeof(a), sizeof(b));
    if(ret) {
        printf("%s %s \n", a, b);
    }
    return ret;
}

int swap(void *a, void *b,
    size_t a_size, size_t b_size)
{
    if (b_size != a_size ) {
        return 0;
    }
    void *tmp = malloc(a_size);
    if(!tmp) {
        return 0;
    }
    memcpy(tmp, a, a_size);
    memcpy(a, b, b_size);
    memcpy(b, tmp, a_size);
    free(tmp); // tmp no longer needed.
    return 1;
}
0
cmaster - reinstate monica On

This swap() function is as good/bad as the memcpy() it uses.

  • If the data structures are just some data structures (of int, float, etc.), it works like a charm.

  • If you pass two pointers to different structures, all hell will break loose. Offending code:

    Foo* myFoo = ...;
    Bar* myBar = ...;
    swap(myFoo, myBar, sizeof(*myFoo));
    

    Note that your compiler won't complain on this as both pointer types are implicitly convertible to the void*s that swap() expects. But the result of compilation will be bullshit.

  • If the structure you copy contains a pointer into itself, that pointer will point into the other object after the swap(). The following struct would be an offender of this:

    typedef struct {
        char* data;
        size_t length, allocatedLength;
        char shortStringBuffer[32];
    } myString;
    

    The idea behind this is, that short strings will be stored in the shortStringBuffer, and data will point to the shortStringBuffer. Strings longer than 31 characters will be stored in dynamically allocated memory, and again be accessible via the data member. It is left as an exercise to the reader to figure out, what precisely happens when you try to copy this thing with a memcpy().

What you must understand, is that memcpy() really only copies bytes, and some data is not invariant to where it is stored. So, each and every use of memcpy() must be accompanied with a proof that it does the correct thing in this particular case. Well, it should. I've never seen such a proof in a comment for some reason...