Search code examples
cpointersvoid-pointerspointer-arithmetic

void* pointer arithmetic, cannot swap float values


recently, i was curious on how qsort() sorts without passing specific type, thus i'm trying to reverse an array without knowing / caring it's type.

i was told you can do pointer arithmetic if you cast void * into a char *.

void swap(void *a, void *b)
{
    char tmp = *(char *)a;
    *(char *)a = *(char *)b;
    *(char *)b = tmp;
}

the function swap() works well with type int & char but not with float nor double.

i don't know why this is the case, but i saw an implementation of swapping with memcpy(), with that, i implented so:

void __swap(void *a, void *b, size_t size)
{
    char buf[size];
    memcpy(buf, a, size);
    memcpy(a, b, size);
    memcpy(b, buf, size);
}

reverse function implementation using swap():

void arr_reverse(void *arr, size_t size, size_t len)
{
    for (size_t i = 0, j = len - 1; i < len / 2; i++, j--)
        swap(arr + (i * size), arr + (j * size));
}

reverse function implementation using __swap():

void arr_reverse(void *arr, size_t size, size_t len)
{
    for (size_t i = 0, j = len - 1; i < len / 2; i++, j--)
        __swap(arr + (i * size), arr + (j * size), size);
}

__swap() works greatly, but i don't know why this is the case, so the question is, what's the difference between swap() and __swap()?

i'm assuming there's something to do with type sizes


Solution

    1. void * arithmetics is an undefined behaviour. It is a GCC extension and it is not allowed by the C standard.

    2. __swap, and swap are doing something completely different. The first is swapping whole chunks of memory of the size len. Latter one is swapping only first chars leaving the rest untouched for longer types (like double or float)

    void swap(void *a, void *b)
    {
        char tmp = *(char *)a;
        *(char *)a = *(char *)b;
        *(char *)b = tmp;
    }
    
    
    void __swap(void *a, void *b, size_t size)
    {
        char buf[size];
        memcpy(buf, a, size);
        memcpy(a, b, size);
        memcpy(b, buf, size);
    }
    
    
    void invalid_arr_reverse(void *vp, size_t size, size_t len)
    {
        unsigned char *arr = vp;
        for (size_t i = 0, j = len - 1; i < len / 2; i++, j--)
            swap(arr + (i * size), arr + (j * size));
    }
    
    
    void arr_reverse(void *vp, size_t size, size_t len)
    {
        unsigned char *arr = vp;
        for (size_t i = 0, j = len - 1; i < len / 2; i++, j--)
            __swap(arr + (i * size), arr + (j * size), size);
    }
    
    void printarr(uint32_t *arr, size_t size )
    {
        while(size--) printf("0x%08x\n" ,(unsigned)*arr++);
    }
    
    
    int main(void)
    {
        uint32_t arr[4] = {0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff};
        uint32_t arr1[4] = {0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff};
    
        invalid_arr_reverse(arr, 4, 4);
        printarr(arr, 4);
        arr_reverse(arr1, 4, 4);
        printarr(arr1, 4);
    }
    

    https://godbolt.org/z/z1sYrPj6G

    Byte swapping function has to be written different way:

    void arr_reverse(void *vp, size_t size, size_t len)
    {
        unsigned char *arr = vp;
        for (size_t i = 0; i < len / 2; i++)
            for(size_t j = 0; j < size; j++)
                swap(arr + (i * size) + j, arr + (len - i -1) * size + j);
    }