Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concatenate two arrays using void pointer (C)

I want to concatenate two arrays of the same type into a single new array with the same type. But the problem is I have to use void pointers, and somehow my code won't work from the third element on. I searched a bit on the internet but seems like noone is having this problem

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

void array_float_fill(float *arr1, float *arr2, int n, int m){
    for(int i = 0;i<n;i++){
        *(arr1+i)=i+n;
    }
    for(int i = 0;i<m;i++){
        *(arr2+i)=i+m;
    }
}

void array_concat(void *arr1, void *arr2, void *arr3, int n, int m,int size){
    for(int i=0;i<n;i++){
        memcpy((arr3+i),(arr1+i),size); 
    }   
    for(int i=0;i<m;i++){
        memcpy((arr3+i+n),(arr2+i),size);
    }
}

int main(int argc, char const *argv[])
{
    int n=10;
    int m=10;
    float f1[n];
    float f2[m];
    array_float_fill(f1,f2,n,m);
    printf("%f",*(f1+3));
    float* f3 = malloc((n+m) * sizeof(float));
    array_concat(f1,f2,f3,n,m,sizeof(float));
    printf("%f",*(f3+3));
    return 0; 
}

I tried it with a for-loop to copy every single element to the new array, because the function will just give me a pointer to the start of the array. Dunno why it doesn't work. Any help and hints would be much appreciated

like image 536
Kuro95 Avatar asked Dec 09 '14 11:12

Kuro95


2 Answers

void* pointer arithmetic ...

... is not actually allowed in standard C. gcc allows it and assumes a base operand size of 1. Thus it follows

Index assumption

You are confusing bytes with indexes when using memcpy.

When (non-standardly) adding integer to void pointers, the integer's meaning (in gcc) is that of a byte-offset. This is unlike a typed pointer, there the compiler will do the appropriate scaling for you:

void *p;
int i;
p[i];   // This will be literally p+i after compiling on your compiler

But for example:

float *p;
int i;
p[i];   // This will be p+i*sizeof(float) after compiling.

So instead of ...

for(int i=0;i<n;i++){
    memcpy((arr3+i),(arr1+i),size); 
}   
for(int i=0;i<m;i++){
    memcpy((arr3+i+n),(arr2+i),size);
}

... you got to write (this is still compiler-specific):

for(int i=0;i<n;i++){
    memcpy((arr3+i*size), (arr1+i*size), size);
}
for(int i=0;i<m;i++){
    memcpy((arr3+i*size+n*size), (arr2+i*size), size);
}

... or go standards-conforming:

for(int i=0;i<n;i++){
    memcpy((char*)arr3+i*size, (char*)arr1+i*size, size);
}
for(int i=0;i<m;i++){
    memcpy((char*)arr3+i*size+n*size, (char*)arr2+i*size, size);
}

By conversion to char*, you enforce a base operand size of 1 byte (more precisely, 1 C byte), which is what you want given the generic nature of your concat function.

However

Note that you can pass arbitrary strides to memcpy, you do not actually need the loops; instead, just do:

memcpy((char*)arr3, arr1, size*n);
memcpy((char*)arr3+n*size, arr2, size*m);

Conversions are only needed where you do arithmetics.

like image 59
Sebastian Mach Avatar answered Sep 29 '22 03:09

Sebastian Mach


You don't have to memcpy in a loop. If you know the size and the length of the arrays you only need two:

void array_concat(void *arr1, void *arr2, void *arr3, int n, int m,int size)
{
    memcpy( arr3 , arr1 , size * n ) ;
    memcpy( ( char* )arr3+( size * n ) , arr2 , size * m ) ;
}

The line ( char* )arr3+( size * n ) gives a pointer to the end of the first part.

The cast to char* is needed because pointer arithmetic doesn't work on void*, so we manually increment the pointer to the correct offset.

For example ( char* )arr3+( n ) would not be correct because the underlying type is float. That is why you pass the size of float and then use it.

Your compiler seems to have an extension that allows you to use void* as it were a char* when using pointer arithmetic.Then you increment it incorrectly here: arr3+i just by the value of
sizeof( char ),instead of sizeof( float ).

like image 21
2501 Avatar answered Sep 29 '22 03:09

2501