【问题标题】:Quadruple pointer and memcpy() in CC中的四重指针和memcpy()
【发布时间】:2017-09-10 05:41:50
【问题描述】:

首先,我知道三重和四重指针是不好的做法而且很丑陋,这不是这个问题的重点,我试图了解它们是如何工作的。我知道使用结构会好得多。

我正在尝试编写一个函数,该函数使用 memmove()memcpy() 对通过引用传递的三重和双指针(或 C 版本)执行一些内存操作。我的memmove() 工作正常,但memcpy() 产生SIGSEGV。这是一个最小的例子

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

#define UNDO_DEPTH 25


void boardSave(int ***board, int game_sz, int ****history) {
    // Shift history to the right
    memmove(*history + 1, *history, (UNDO_DEPTH - 1) * sizeof(**history));
    // Copy board into history
    for (int row = 0; row < game_sz; ++row) {
        memcpy((*history)[0][row], (*board)[row], game_sz * sizeof((**board)[row]));
    }
}

int main(){
    // Game
    int game_sz = 5;
    // Allocate array for the board
    int **board = calloc(game_sz, sizeof(int *));
    for (int i = 0; i < game_sz; ++i) board[i] = calloc(game_sz, sizeof(int));
    // Allocate array for the history
    int ***history = calloc(UNDO_DEPTH, sizeof(int **));
    for (int i = 0; i < UNDO_DEPTH; ++i) {
        history[i] = calloc(game_sz, sizeof(int *));
        for (int j = 0; j < game_sz; ++j) {
            history[i][j] = calloc(game_sz, sizeof(int));
        }
    }
    board[0][0] = 1;
    boardSave(&board, game_sz, &history);
}

这里boardSave()的目的是将board复制到history[0]上。我究竟做错了什么?为什么这会导致分段错误?

【问题讨论】:

  • memmove(**history + 1, *history,... 中不同的间接深度相当可疑。
  • @aschepler 我的意图是移动history 1“槽到右边”的所有元素,消除最后一个(在本例中为第 25 个)元素。
  • memmove(**history + 1, **history, (UNDO_DEPTH - 1) * sizeof(***history)); 似乎修复了它。需要多发短信
  • 请注意,称某人为Three Star Programmer 不是一个批准条款。四星程序员可能更不受欢迎。我的思绪被震撼了;我不想维护你的代码。
  • 是的;我要让其他任何出现并发现这一点的人都知道你在深水中,问题并不令人惊讶。你最好不知道,IMO。

标签: c pointers pass-by-reference indirection


【解决方案1】:

我了解您想挑战指针。 我想提供一个利用单指针的解决方案。 事实上,你根本不需要使用指针。

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

const int game_sz = 5;
#define UNDO_DEPTH 25



void boardSave(int *board[game_sz], int game_sz, int *history[UNDO_DEPTH]
[game_sz]) 
{
    int i,j,k;

    for( i = 0; i < UNDO_DEPTH - 1; i++)
       for( j = 0; j < game_sz; j ++ )
          for( k = 0; j < game_sz; j ++ )
            history[i+1][j][k] = history[i][j][k];

    for( i = 0; i < game_sz - 1; i++)
       for( j = 0; j < game_sz; j++ )
           history[0][i][j] = board[i][j];
}

int
main(void)
{
  int *board[game_sz];
  int *history[UNDO_DEPTH][game_sz];
  int i, j;


  for (i = 0; i < game_sz; ++i) 
    board[i] = calloc(game_sz, sizeof(int));
  board[0][0] = 1;

  // Allocate array for the history
  for ( i = 0; i < UNDO_DEPTH; ++i)
      for ( j = 0; j < game_sz; ++j)
        history[i][j] = calloc(game_sz, sizeof(int));


  boardSave( board, game_sz, history);

  return 0;
 }

【讨论】:

    【解决方案2】:

    main 函数中,您使history 指向UNDO_DEPTH 指针数组,每个指针都指向具有自己分配的板。由于memmove 移动了一个连续的内存块,因此您无法使用 memmove 移动所有这些板的内容。

    但是,您可以将 history 数组中的指针向下移动,而保持板分配不变。

    仅执行一次 memmove 将要求您释放最后一个被洗牌的板的内存,并为新板分配内存。但是您可以通过将最后一个指针移动到开头来回收该内存。

    现在,无需将boardhistory 的地址传递给boardSave 函数。它只会无缘无故地使您的代码更加复杂。更简单的版本是:

    void boardSave(int **board, int game_sz, int ***history)
    {
    // Save the last board
        int ** last_board = history[UNDO_DEPTH - 1];
    
    // Shuffle down all the boards
        memmove( &history[1], &history[0], (UNDO_DEPTH - 1) * sizeof history[0] );
    
    // Put the old last board on the front
        history[0] = last_board;
    
    // Copy board into front of history
        copy_board( game_sz, history[0], board );
    }
    
    // Put a prototype for this earlier in the code. I think it makes
    // the boardSave function clearer to use a separate function for this
    // operation, which you might end up using on its own anyway.
    //
    void copy_board( int game_sz, int **dest, int **src )
    {
        for(int row = 0; row < game_sz; ++row)
            memcpy(dest[row], src[row], game_sz * sizeof dest[0][0]);
    }
    

    就我个人而言,我更愿意在最后一个函数中避免使用 memcpy,而只编写一个显然正确的简单循环。编译器会优化它以使用 memcpy,但不会在 memcpy 参数中出错:

        for(int row = 0; row < game_sz; ++row)
            for (int col = 0; col < game_sz; ++col)
                dest[row][col] = src[row][col];
    

    类似的 cmets 实际上也适用于 memmove 的使用。

    我还会在函数签名中使用const,这样如果我不小心切换了“dest”和“src”参数,就会产生编译器错误。但为了简单起见,我在这个阶段省略了。

    main 中调用现在是:

    boardSave(board, game_sz, history);
    

    如果你真的想传递指针进行练习,那么我会在函数的开头“删除”它们:

    void complicated_boardSave(int ***p_board, int game_sz, int ****p_history)
    {  
        int *** history = *p_history;
        int  ** board = *p_board;
    
        // rest of code the same
    

    【讨论】:

    • 这是否真的会改变 main 中变量的值,即使我没有通过引用传递?
    • @BernardoMeurer 您不想更改 main 中的变量 - 您想更改这些变量指向的内存
    • 非常感谢您为帮助我理解这一点所付出的耐心和努力。希望我能给你一杯啤酒。
    • 天冷了我就拿,天热了,池塘那边的你们可以留着:)
    • 我不明白这条线的目的 history[0] = last_board; history[0] 将很快用 board 重写。我认为您不需要变量 last_board;
    猜你喜欢
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-15
    • 2016-09-02
    相关资源
    最近更新 更多