使用void指针(C)连接两个数组

时间:2014-12-09 11:51:44

标签: c arrays function concatenation memcpy

我想将两个相同类型的数组连接成一个具有相同类型的新数组。但问题是我必须使用void指针,不知怎的,我的代码不会从第三个元素开始工作。我在互联网上搜索了一下,但似乎没有人遇到这个问题

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

我尝试使用for循环将每个元素复制到新数组,因为该函数只会给我一个指向数组开头的指针。不知道为什么它不起作用。任何帮助和提示将不胜感激

5 个答案:

答案 0 :(得分:4)

void*指针算术...

...实际上不允许在标准C中使用.gcc允许它并假设基本操作数大小为1.因此它遵循

指数假设

使用memcpy时,您会将字节与索引混淆。

当(非标准地)将整数添加到void指针时,整数的含义(在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<n;i++){
    memcpy((arr3+i),(arr1+i),size); 
}   
for(int i=0;i<m;i++){
    memcpy((arr3+i+n),(arr2+i),size);
}

...你必须写(这仍然是编译器特定的):

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

......或符合标准:

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

通过转换为char*,您可以强制执行1个字节(更确切地说,1个C字节)的基本操作数大小,这是您想要的concat函数的通用性质。

然而

请注意,您可以将任意步幅传递给memcpy,实际上并不需要循环;相反,只是做:

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

只有在你做算术时才需要转换。

答案 1 :(得分:3)

你不必在循环中记忆。如果你知道数组的大小和长度,你只需要两个:

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只是通过
sizeof的值(char),而不是sizeof(float)。

答案 2 :(得分:1)

您可以使用单个memcpy复制整个数组。检查memcpy的签名:

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

它会将n个连续字节从src复制到dest,就像听起来一样简单。数组是一个连续的内存块,因此您只需调用memcpy即可复制整个数组。

我在下面的代码中解决了这个问题以及其他一些问题:

  • 通过索引访问数组而不是执行指针运算,这很容易出错。

  • <强> Constness

  • size_t而不是int用于大小和数组索引。请参阅 Why size_t matters

  • 在复制之前传递输出缓冲区大小并检查它是个好习惯,以防止缓冲区溢出。

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

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(
    void *out_arr,
    size_t out_arr_size,
    const void *in_arr1,
    size_t in_arr1_size,
    const void *in_arr2,
    size_t in_arr2_size)
{
    const size_t total_size = in_arr1_size + in_arr2_size;
    // BE AWARE that `assert` will be gone if you define `NDEBUG`
    // (or compile with `-DNDEBUG`).
    // This assertion guarantees we have enough space before copying.
    assert(out_arr_size >= 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; 
}

答案 3 :(得分: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);

答案 4 :(得分:-1)

尝试以下

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 <stdio.h>
#include <stdlib.h>
#include <string.h>

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

不要忘记释放动态分配的数组。此外,您还应该包含标头<string.h>,其中memcpy已声明。

考虑到这个主要宣言

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

错了。 正确的声明看起来像

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