C

时间:2017-04-14 00:04:27

标签: c pointers pass-by-reference indirection

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

我正在尝试编写一个函数,它使用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]。我究竟做错了什么?为什么会导致分段错误?

2 个答案:

答案 0 :(得分:3)

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

如果你想要传递指针进行练习,那么我会在函数开始时“取消指向”它们:

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

答案 1 :(得分:0)

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

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