首先,我知道三重和四重指针是不好的做法并且很难看,这不是这个问题的重点,我试图了解它们是如何工作的。我知道使用结构会好得多。
我正在尝试编写一个函数,它使用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]
。我究竟做错了什么?为什么会导致分段错误?
答案 0 :(得分:3)
在main
函数中,您使history
指向一个UNDO_DEPTH
指针数组,每个指针指向一个具有自己分配的板。由于memmove
移动了连续的内存块,因此无法使用memmove移动所有这些板的内容。
但是,您可以向下移动history
数组中的指针,保持电路板分配不受影响。
只需执行一次memmove就会要求您释放最后一块电路板的内存,并为新电路板分配内存。但是你可以通过将最后一个指针移动到开头来回收那个内存。
现在,无需将board
和history
的地址传递给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;
}