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 就需要您释放最后洗牌的棋盘的内存,并为新棋盘分配内存。但是您可以通过将最后一个指针移动到开始来回收该内存。
现在,不需要将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);
如果你真的想传递指针来练习,那么我会 "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;
}
首先,我知道三重和四重指针是不好的做法而且很丑,这不是这个问题的重点,我想了解它们是如何工作的。我知道使用结构会好得多。
我正在尝试编写一个函数,该函数使用 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 就需要您释放最后洗牌的棋盘的内存,并为新棋盘分配内存。但是您可以通过将最后一个指针移动到开始来回收该内存。
现在,不需要将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);
如果你真的想传递指针来练习,那么我会 "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;
}