【问题标题】:ansi-c, better copy array elements to another array?ansi-c,更好地将数组元素复制到另一个数组?
【发布时间】:2011-04-16 21:08:39
【问题描述】:

有没有办法做我在第 2 行和第 3 行所做的事情,并且与第 1 行类似?

如果我只放第 1 行,那么“a”和“one.index1”都将指向相似的位置,这是我不想要的。我真正想要的是由第 2 行和第 3 行完成。那么,这是唯一的方法,还是任何人都可以提供更好的方法?

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

typedef struct
{
 int *index1;
} data;

void doo(int *);

int main(int argc, char *argv[])
{
 int *a = (int *) malloc(10*sizeof(int));
 int i;

 for(i=0; i<10; i++)
 {
  a[i] = 2*i;
 }

 doo(a);

 data one;
 //one.index1 = a;                           // Line 1
 /*******************/
 one.index1 = (int *) malloc(5*sizeof(int)); // Line 2
 for(i=0; i<5; i++) one.index1[i] = a[i];    // Line 3
 /*******************/

 printf("%d\n", one.index1[4]);

 free(a);

 printf("%d\n", one.index1[4]);

 free(one.index1);
 return 0;
}

void doo(int *b)
{
 b = (int *) realloc(b, 5*sizeof(int));
 return;
}

提前致谢!

【问题讨论】:

    标签: c


    【解决方案1】:

    doo 不会按原样工作。它需要是:

    void doo(int **b) 
    { 
     *b = (int *) realloc(*b, 5*sizeof(int)); 
     return; 
    } 
    

    并由

    调用
     doo(&a);
    

    如果 realloc 发生在 malloc 之后立即发生,您的原始版本可能偶尔会意外工作,因此有空间来扩展内存块。但这不应该指望。

    现在,回答您的实际问题,由于我们正在处理简单的数据项(即整数),您可以使用memmove()memcpy() 来复制它们:(如果内存块重叠,memmove 是安全的;memcpy不是,但这不是问题)

      one.index1 = (int *) malloc(5*sizeof(int)); // Line 2 
      memcpy(one.index1, a, sizeof(int) * 5); 
    

    至于 memmove/memcpy 的效率,这几乎是一个未知领域。 memmove 比 memcpy 做了更多的范围检查,所以它会比 memcpy 慢一点。至于 memcpy 与循环,很难说。 memcpy 的开销有点大,但它被称为 a lot,因此编译器供应商有一个人会花费 很多 时间来确保它尽可能快。

    但是请注意,如果要复制的元素数量很少,固定数量,最快的方法就是直接复制它们:

      one.index1 = (int *) malloc(5*sizeof(int)); // Line 2 
      one.index1[0] = a[0];
      one.index1[1] = a[1];
      one.index1[2] = a[2];
      one.index1[3] = a[3];
      one.index1[4] = a[4];
    

    【讨论】:

    • 如果我总是确定 realloc 会缩小大小并且永远不会增加它会怎样。在这种情况下,我的选择是否仍然有效?
    【解决方案2】:

    一些小问题。

    如果您在调用函数之前定义函数(如果它们在同一个源文件中,即),生活会更轻松;这样您就不必担心保持声明/定义同步。

    不要转换malloc()的结果。从 C89 开始,您不需要这样做,如果您忘记 #include stdlib.h 或在范围内没有 malloc() 的原型,这样做可能会抑制有用的诊断(尽管较新的编译器正在捕获此错误即使有演员表,因为它经常发生如此血腥)。

    在您分配的东西上使用sizeof 运算符,而不是类型;同样,它让生活更简单,因为您不必保持声明和 malloc() 调用同步。

    如果你想让一个函数修改一个指针参数,你必须传递一个指向那个指针的指针:

    void doo(int **p)
    {
      *p = realloc(*p, 5 * sizeof **p); 
    }
    

    否则对形参p的改动不会反映在实参中。

    如果你抽象出你的内存管理功能,生活也会更轻松;您可以用一个函数调用替换第 2 行和第 3 行:

    int *copy(int * const source, size_t len)
    {
      int *dest = malloc(len * sizeof *dest);
      if (dest)
        memcpy(dest, source, len * sizeof *source);
      return dest;
    }
    

    你称之为

    one.index1 = copy(a, len);
    

    【讨论】:

      【解决方案3】:

      您可以使用可能高度优化的memmove/memcpy函数:

      const size_t size = 5*sizeof(int);
      one.index = (int *) malloc(size);
      memcpy(one.index, a, size);
      

      当然,你可以把它包装成一个函数:

      int *getCopy(const int *source, size_t count)
      {
        int *result = (int *)malloc(count * sizeof(int));
        if (!result)
          return NULL;
        memcpy(result, source, count * sizeof(int));
        return result;
      }
      
      // Usage
      one.index = getCopy(a, 5);
      

      【讨论】:

        【解决方案4】:

        这取决于您具体要避免的第 2 行和第 3 行。如果您想避免循环,请使用 memcpy,尽管大多数编译器应该用等效于 memcpy 调用的代码替换您的循环。如果你想要一个单行操作,你可以编写一个静态函数,它接受 one.index1 的地址、数组 a 的大小和 a 作为参数。

        int * f (int **int_pp, size_t len, int const *a) {
            *int_pp = (int *) malloc (len * sizeof (int));
            if ( *int_pp != NULL ) {
               memcpy (*int_pp, a, len);
            }
            return *int_pp;
        }
        

        别忘了检查 malloc 是否返回 NULL!

        如果您想避免调用 malloc,我认为唯一的选择是留出一块您手动管理的内存。不过,尝试自己管理内存几乎是不值得的。

        【讨论】:

          【解决方案5】:

          您可以使用 memmove。请务必在开始时#include &lt;string.h&gt;

          将第 2 行和第 3 行替换为,

          one.index1 = malloc( 5*sizeof(int) );
          memmove(one.index1,a, 5*sizeof(int) );
          

          例如,在这里考虑 a & c:

          int *a= (int *) malloc(10*sizeof(int));
          for(i=0; i<10; i++)
          {
              a[i] = 2*i;
          }   
          int *c= (int *) malloc(10*sizeof(int));
          memmove(c,a,10*sizeof(int));
          

          现在 a 和 c 都是单独的指针,指向“相同的数据”& 可以单独释放。

          a[0]=0 c[0]=0
          a[1]=2 c[1]=2
          a[2]=4 c[2]=4
          a[3]=6 c[3]=6
          a[4]=8 c[4]=8
          a[5]=10 c[5]=10
          a[6]=12 c[6]=12
          a[7]=14 c[7]=14
          a[8]=16 c[8]=16
          a[9]=18 c[9]=18
          

          这种新方法效率更高吗?实际上,它是。比较程序运行所用的时间:(我用的是long long类型而不是int,得到明显的时间差)

          旧方法:

          $ time ./a.out
          
          real    0m0.249s
          user    0m0.178s
          sys     0m0.070s
          

          新方法:

          $ time ./a.out
          
          real    0m0.164s
          user    0m0.095s
          sys     0m0.068s
          

          【讨论】:

          • 谢谢。你知道它是否比循环更有效?
          • 理论上应该是这样,因为整个内存块被作为一个整体复制,而之前的方法在循环体本身中涉及 5 个赋值和 10 个数组下标分辨率。
          • @jkl,查看我的回答以了解您的效率问题。
          猜你喜欢
          • 2018-04-19
          • 1970-01-01
          • 1970-01-01
          • 2021-11-26
          • 2018-02-17
          • 1970-01-01
          • 2014-07-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多