【问题标题】:How does memmove on array of pointers in C/C++ work?C/C++ 中指针数组上的 memmove 是如何工作的?
【发布时间】:2020-03-16 04:25:40
【问题描述】:

我有一个这样的数组:

double **Arr = malloc(N*sizeof(double*));
for(int j = 0; j < N-1; j++)
  Arr[j] = malloc(M*sizeof(double))

这些数组充满了值。我想移动一些数组(最后的 N-i 个)。这两行有什么区别吗?

for(int j = i; j < N-1; j++)
  for(int k = 0; k < M; k++)
    Arr[j][k] = Arr[j+1][k];

或

memmove(&(Arr[i]), &(Arr[i+1]), (N-1-i)*sizeof(double*));

【问题讨论】:

  • 你没有数组数组,你有一个指针数组。想想复制数组元素和复制指针的区别。
  • memmove 基本上是 memcpy,但它允许 src 和 dest 指针之间重叠(这意味着它也会更慢)。
  • @molbdnilo 他没有指针数组,他有一个指向指针的指针。
  • 考虑使用double (*arr)[M] = malloc( sizeof(double[N][M]) );,然后使用memcpy(arr[i], arr[i+1], sizeof(double[M]));。它对缓存更友好,也更容易阅读。
  • @FlorianAubin 将您的 C 编译器更新为本世纪推出的标准编译器,您可以。

标签: c memmove


【解决方案1】:

第一个复制M数字N-1-i次,第二个复制N-1-i指针一次。
也就是说,如果double 和double* 的大小相同,则第一个复制M 的内存是第二个的两倍。
(而且对缓存不太友好。)

简化为两个指针:

 double *arr = malloc(M * sizeof(double));
 double *arr2 = malloc(M * sizeof(double));

之间的区别是一样的

 for (int k = 0; k < M; k++) arr2[k] = arr[k];

和

arr2 = arr;

你应该使用哪一个取决于你所追求的;复制指针或复制元素。

【讨论】:

    【解决方案2】:

    这两行有什么区别吗?

    让我们来:

    memmove(&(Arr[i]), &(Arr[i+1]), (N-1-i)*sizeof(double*));
    

    基本上,它的作用可以通过将指定数量的字节从一个指针逐字节复制到另一个指针来表示。所以可以写成这样希望正确的代码:

    for (size_t j = 0; j < (N-1-i)*sizeof(double*); ++j) {
        *( ((char*)&Arr[i]) + j ) = *( ((char *)(&Arr[i + 1]) + j );
    

    噗,这在技术上有很大的不同,但让我们删除 sizeof(double*) 和演员:

    for (size_t j = 0; j < N - 1 - i; ++j)
        *(&Arr[i] + j) = *(&Arr[i + 1] + j);
    

    &amp;Arr[i] 等于 &amp;*(Arr + i) 等于 Arr + i,所以:

    for (size_t j = 0; j < N - 1 - i; ++j) {
        *(Arr + i + j) = *(Arr + i + j + 1);
    

    *(a + i) 等于 a[i],所以我们可以:

    for (size_t j = 0; j < N - 1 - i; ++j) {
        Arr[i + j] = Arr[i + j + 1];
    

    由于i + j在两边,j从0开始,我们可以从i开始:

    for (size_t j = i; j < N - 1; ++j) {
        Arr[j] = Arr[j + 1];
    

    现在这会将指针值(!)从Arr[j + 1] 复制到Arr[j]。这与复制指针指向的值有很大不同:

    for(int j = i; j < N - 1; j++)
        for(int k = 0; k < M; k++)
             Arr[j][k] = Arr[j + 1][k];
    

    另一方面,上面的代码可以翻译成:

    for(int j = i; j < N - 1; j++)
        memcpy(Arr[j], Arr[j + 1], M * sizeof(double));
      //^^^^^^ or memove, but I guess not
    

    这些数组充满了值

    你展示的初始化例程有这个循环:

    for(int j = 0; j < N-1; j++)
       Arr[j] = malloc(M*sizeof(double))
    

    保持理智并假设N &gt; 1,然后Arr[N - 1] 未初始化。因此,当j = N - 2 访问未初始化的值时,在上述循环中访问Arr[j + 1] 以获取最后一个循环。并且循环for(int k = 0; k &lt; M; k++)Arr[j][k] = Arr[j + 1][k]; 取消引用Arr[j + 1][k] 中的未初始化指针并且是未定义的行为。

    【讨论】:

      【解决方案3】:

      这两行有什么区别吗?

      是的,它们非常不同。第二个是内存泄漏。

      解释:

      初始化后如下:

      double **Arr = malloc(N*sizeof(double*));
      for(int j = 0; j < N; j++)
        Arr[j] = malloc(M*sizeof(double))
      

      (注意:我将 N-1 更改为 N,因为我认为这是一个错误)

      分配的内存现在看起来像:

      当你这样做时:

      for(int j = i; j < N; j++)
        for(int k = 0; k < M; k++)
          Arr[j][k] = Arr[j+1][k];
      

      您将双精度数从水平数组复制到上面的水平数组。喜欢:

      当你这样做时:

      memmove(&(Arr[i]), &(Arr[i+1]), (N-1-i)*sizeof(double*));
      

      您将垂直数组中的双指针向上复制一个位置。喜欢:

      所以这两个代码块执行的操作非常不同。

      memmove 很糟糕,因为它会泄漏内存。

      【讨论】:

        猜你喜欢
        • 2021-07-13
        • 1970-01-01
        • 2011-11-01
        • 2021-07-06
        相关资源
        最近更新 更多