C 中的四重指针和 memcpy()

Quadruple pointer and memcpy() in C

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

我正在尝试编写一个函数,该函数使用 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()的objective是将board复制到history[0]上。我究竟做错了什么?为什么这会导致分段错误?

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];

类似的评论实际上适用于 memmove 的使用。

我还会在函数签名中使用一些 const,这样如果我不小心切换了 "dest" 和 "src" 参数,就会生成编译器错误。但我在这个阶段简单地忽略了它。

main 中,调用现在是:

boardSave(board, game_sz, history);

如果你真的想传递指针来练习,那么我会 "de-point" 在函数的开头:

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

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

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