Объединение двух массивов с использованием указателя void (C)

Я хочу объединить два массива одного типа в один новый массив с тем же типом. Но проблема в том, что я должен использовать указатели void , и каким-то образом мой код не будет работать с третьим элементом. Я немного искал в Интернете, но похоже, что у этой проблемы нет.

 #include  #include  #include  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; } с #include  #include  #include  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; } 

Я попробовал его с помощью -loop для копирования каждого отдельного элемента в новый массив, потому что функция просто даст мне указатель на начало массива. Не знаю почему это не работает. Любая помощь и подсказки будут высоко оценены

void* указатель арифметики …

… на самом деле не разрешено в стандартном C. gcc допускает его и принимает размер операнда базы 1. Это следует из

Предположение индекса

Вы используете пустые байты с индексами при использовании memcpy .

Когда (нестандартно) добавление целых чисел к указателям void , значение integer (в gcc) является значением байта смещения. Это не похоже на типизированный указатель, там компилятор сделает для вас соответствующее масштабирование:

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

Но, например:

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

Поэтому вместо …

 for(int i=0;i 

... вы должны писать (это все еще зависит от компилятора):

 for(int i=0;i 

... или придерживаться стандартов:

 for(int i=0;i 

При преобразовании в char* вы устанавливаете размер базового операнда 1 байт (точнее, 1 C байт), что вам нужно, учитывая общий характер вашей функции concat.

тем не мение

Обратите внимание, что вы можете передавать произвольные шаги в memcpy , вам фактически не нужны циклы; вместо этого просто выполните:

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

Конверсии нужны только там, где вы делаете арифметику.

Вам не нужно memcpy в цикле. Если вы знаете размер и длину массивов, вам нужны только два:

 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 ) ; } 

Строка ( char* )arr3+( size * n ) дает указатель на конец первой части.

Приведение в char * необходимо, поскольку арифметика указателя не работает на void *, поэтому мы вручную увеличиваем указатель до правильного смещения.

Например ( char* )arr3+( n ) не будет правильным, потому что базовый тип – float. Вот почему вы передаете размер float, а затем используете его.

У вашего компилятора, похоже, есть расширение, которое позволяет использовать void * как бы char * при использовании арифметики указателя. arr3+i вы увеличиваете его здесь неправильно: arr3+i просто по значению
sizeof (char), вместо sizeof (float).

Вы можете скопировать весь массив с помощью одной memcpy . Проверьте подпись memcpy :

 void *memcpy(void *dest, const void *src, size_t n); 

Он будет копировать n смежных байтов из src в dest , так же просто, как кажется. Массив – это непрерывный блок памяти, поэтому вы можете скопировать весь массив, используя один вызов memcpy .

Я исправил это и несколько других вещей в коде ниже:

  • Доступ к массивам по индексам, а не по арифметике указателей, что легко ошибиться.

  • константность

  • Использование size_t вместо int для размеров и индексов массива. См. Раздел Почему size_t имеет значение .

  • Хорошая привычка передавать размер выходного буфера и проверять его перед любым копированием, чтобы предотвратить переполнение буфера.

 #include  #include  #include  #include  void array_float_fill(float *arr1, float *arr2, size_t n, size_t m) { for (size_t i = 0; i= total_size); memcpy(out_arr, in_arr1, in_arr1_size); memcpy((char *)out_arr + in_arr1_size, in_arr2, in_arr2_size); // The cast to `char *` above is important for portability. // Arithmetic on a pointer to void is a GNU extension. // See http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html } void dump_array(const char *name, float *array, size_t num_elems) { printf("%s = ", name); for (size_t i = 0; i < num_elems; i++) { printf("%f ", array[i]); } printf("\n"); } int main() { const size_t n = 10; const size_t m = 10; float f1[n]; float f2[m]; array_float_fill(f1, f2, n, m); const size_t f3_size = (n + m) * sizeof(float); float *f3 = malloc(f3_size); array_concat(f3, f3_size, f1, sizeof(f1), f2, sizeof(f2)); // Show the results dump_array("f1", f1, n); dump_array("f2", f2, m); dump_array("f3", f3, n + m); return 0; } 

Арифметические операции не могут выполняться на указателях void. Так как указатели void не имеют фиксированных типов данных, таких как int или float, они не знают, с какими значениями они должны увеличиваться или уменьшаться при арифметических операциях. Например, когда вы увеличиваете указатель целых чисел, значение автоматически увеличивается на 4 при каждом приращении. Точно так же указатель символа будет увеличен на 1 при каждом приращении. Здесь, в вашем примере, когда операция memcpy пытается скопировать значение из arr1 в arr3, он не сможет удалить ссылку arr3, следовательно, проблема. Как правило, всякий раз, когда такие операции выполняются с указателями void, сначала их нужно отменить. Отмените ссылку на плавающий указатель, см. Ниже

  memcpy(((float*)arr3+i),((float*)arr1+i),size); memcpy(((float*)arr3+i+n),((float*)arr2+i),size); с  memcpy(((float*)arr3+i),((float*)arr1+i),size); memcpy(((float*)arr3+i+n),((float*)arr2+i),size); 

Попробуйте следующее

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

Вот как может выглядеть программа

 #include  #include  #include  void array_float_fill( float *arr1, float *arr2, size_t n, size_t m ) { for ( size_t i = 0; i < n; i++ ) { *( arr1 + i ) = i + n; } for ( size_t i = 0; i < m; i++ ) { *( arr2 + i ) = i + m; } } void array_concat( const void *arr1, const void *arr2, void *arr3, size_t n, size_t m, size_t size ) { memcpy( arr3, arr1, n * size ); memcpy( ( char * )arr3 + n * size, arr2, m * size ); } int main( void ) { size_t n = 10; size_t m = 10; float f1[n]; float f2[m]; array_float_fill( f1, f2, n, m ); printf( "%f\n",*( f1 + 3) ); float *f3 = malloc( ( n + m ) * sizeof( float ) ); array_concat( f1, f2, f3, n, m, sizeof( float ) ); printf( "%f\n", *( f3 + 3 ) ); free( f3 ); return 0; } 

Выход

 13.000000 13.000000 

Не забудьте освободить динамически выделенный массив. Также вы должны включить header где объявлен memcpy .

Учтите, что эта декларация основных

 int main(int argc, char const *argv[]) 

неправильно. Правильная декларация выглядит так:

 int main(int argc, char *argv[])