strcpy:检测到源和目标缓冲区重叠
strcpy: detected source and destination buffer overlap
这是我对康威生命游戏的实现。
我目前正在尝试实施重复检测,它允许程序检测周期长达 4 代的重复。为此,我使用了一个名为 statelist
的列表,该列表旨在存储当前状态后面的 4 个状态(状态表示为由函数 serialize_board()
创建的字符串)。为了将新状态推送到 statelist
(并可能丢弃路径中的最后一个状态),我实现了 push_list()
函数。但是在push_list()
函数中,出现错误:
2017-07-18 13:26:32.496180+0100 Assignment 3[38082:4064098] detected source and destination buffer overlap
函数初始化的注释中描述了 push_list
所需的功能。我不明白字符串如何重叠。每个字符串的statelist
和nrows*ncols+1
字节内存我都分配了足够的内存,也就是每个状态字符串的大小。对于冗长的程序感到抱歉,但是当这么多功能相互依赖时很难缩短它。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define MAX_COORDINATE_SIZE 50
#define MAX_FILENAME_SIZE 20
#define MAX_GENERATIONS 10
#define MAX_REPETITION_PERIOD 4
struct coord{ //Holds coordinates to a cell
int x;
int y;
};
struct cell{
int pop; //Populated
// char pos; //C: corner, T: top, R: right, B: bottom, L: left
int age;
};
struct coord *read_init(FILE *fp, int *i);
static int read_line(FILE *fp, char *line, int max_length);
struct coord read_coords(char *line);
struct cell **create_board(int x, int y);
void populate_board(struct coord *coords, struct cell ***board, int *n);
struct cell new_cell(int x, int y, int pop, int age);
struct cell **start_game(FILE *fp, int nrows, int ncols);
void print_board(struct cell **board, int nrows, int ncols);
void init_board(int nrows, int ncols, struct cell ***board);
int live(int i, int j, struct cell **board, int nrows, int ncols);
void step(struct cell ***board, int nrows, int ncols);
void push_list(char **list, int len, char *state);
int repetitive(char **list, int len);
char *serialize_board(struct cell **board, int nrows, int ncols);
int main(int argc, const char * argv[]) {
int gens;
char gens_string[MAX_GENERATIONS];
if(argc != 3){
fprintf(stderr, "Usage: %s <seed-file> <generations>\n<seed-file> can me up to %d characters long\n", argv[0], MAX_FILENAME_SIZE);
exit(1);
}
FILE *fp = fopen(argv[1], "r");
strncat(gens_string, argv[2], MAX_GENERATIONS);
gens = atoi(gens_string);
int nrows = 10;
int ncols = 10;
int repetitions;
//allocate memory and set up pointers for statelist
char **statelist;
statelist = malloc((MAX_REPETITION_PERIOD+1) * sizeof(char *) + (MAX_REPETITION_PERIOD+1+1) * nrows * ncols); //MAXREP+1 is the number of strings we want to store. The other +1 is to fit in the null temrinator
char *firstrow = statelist + MAX_REPETITION_PERIOD+1;
for(int i = 0; i < MAX_REPETITION_PERIOD+1; i++){
statelist[i] = firstrow + i * nrows * ncols;
}
struct cell **board= start_game(fp, nrows, ncols);
print_board(board, nrows, ncols);
push_list(statelist, MAX_REPETITION_PERIOD+1, serialize_board(board, nrows, ncols));
struct timespec t;
t.tv_sec = 0;
t.tv_nsec = 500000000L;
for(int i = 0; i < gens; i++){
step(&board, nrows, ncols);
nanosleep(&t, NULL);
print_board(board, nrows, ncols);
push_list(statelist, MAX_REPETITION_PERIOD+1, serialize_board(board, nrows, ncols));
if((repetitions = repetitive(statelist, MAX_REPETITION_PERIOD+1))){
printf("Repetition detected (%d)\n", repetitions);
exit(1);
}
}
return 0;
}
struct coord *read_init(FILE *fp, int *n){ //Takes in filename and returns list of coordinates to be populated
char raw_n[100];
struct coord *coords;
char *line;
line = malloc(sizeof(char)*MAX_COORDINATE_SIZE);
read_line(fp, raw_n, 100); // get the first line of the file (number of popuated cells)
*n = atoi(raw_n);//make an int out of raw_n
coords = malloc(sizeof(struct coord)*(*n)); //Allocate memory for each coord
for(int i = 0; i<(*n); i++){ // for each line in the file (each populated cell)
read_line(fp, line, MAX_COORDINATE_SIZE);
coords[i] = read_coords(line); //Put coordinates in coords
line[0] = '[=11=]';
}
return coords; // return coordinates
}
static int read_line ( FILE *fp, char *line, int max_length)
{
int i;
char ch;
/* initialize index to string character */
i = 0;
/* read to end of line, filling in characters in string up to its
maximum length, and ignoring the rest, if any */
for(;;)
{
/* read next character */
ch = fgetc(fp);
/* check for end of file error */
if ( ch == EOF )
return -1;
/* check for end of line */
if ( ch == '\n' )
{
/* terminate string and return */
line[i] = '[=11=]';
return 0;
}
/* fill character in string if it is not already full*/
if ( i < max_length )
line[i++] = ch;
}
/* the program should never reach here */
return -1;
}
struct coord read_coords(char *line){ // Returns coordinates read from char *line
struct coord c;
char *x;
char *y;
x = malloc(sizeof(char)*MAX_COORDINATE_SIZE);
y = malloc(sizeof(char)*MAX_COORDINATE_SIZE);
int i = 0;
do{
x[i] = line[i]; //Get the x coordinate
i++;
}while(line[i] != ' ');
i++;
do{
y[i-2] = line[i];
i++;
}while(line[i] != '[=11=]');
c.x = atoi(x)-1;
c.y = atoi(y)-1;
return c;
}
void init_board(int nrows, int ncols, struct cell ***board){
*board = malloc(nrows * sizeof(*board) + nrows * ncols * sizeof(**board));
//Now set the address of each row or whatever Whosebug says
struct cell * const firstrow = *board + nrows;
for(int i = 0; i < nrows; i++)
{
(*board)[i] = firstrow + i * ncols;
}
for(int i = 0; i < nrows; i++){ //fill the entire board with pieces
for(int j = 0; j < ncols; j++){
(*board)[i][j] = new_cell(i, j, 0, 0);
}
}
}
struct cell new_cell(int x, int y, int pop, int age){ //Return new populated or non-populated cell with specified coordinates
struct cell c;
c.pop = pop;
c.age = age;
return c;
}
struct cell **start_game(FILE *fp, int nrows, int ncols){ //x,y are no of rows/columns, fn is filename
int n; // n is the number of populated cells specified in the seed
struct coord *coords = read_init(fp, &n); // get the list of coords to populate board with
struct cell **board;
init_board(nrows, ncols, &board); // Set up the board
populate_board(coords, &board, &n); //populate the cells specified in the seed
return board;
}
void populate_board(struct coord *coords, struct cell ***board, int *n){
for(int i = 0; i < *n; i++){
(*board)[coords[i].x][coords[i].y].pop = 1; //populate the cell
}
}
void print_board(struct cell **board, int nrows, int ncols){
printf("--------------------\n");
for(int i = 0; i<nrows; i++){
for(int j = 0; j<ncols; j++){
if(board[i][j].pop == 1){
printf("%d ", board[i][j].age);
}else if(board[i][j].pop == 0){
printf(" ");
}else{
printf("\n\nERROR!");
exit(0);
}
}
printf("\n");
}
printf("--------------------");
printf("\n");
}
int live(int i, int j, struct cell **board, int nrows, int ncols){ //returns 1 if cell will live, 0 if cell will die,
int status = board[i][j].pop; // status = 1 if alive, 0 if dead
int n=0; //neighbours
//Counting all the neighbours
if(i == 0 && j == 0){ //if top left cornerpiece
if(board[i][j+1].pop) { n++; } // east
if(board[i+1][j+1].pop){ n++; } // southeast
if(board[i+1][j].pop) { n++; } // south
}
else if(i == 0 && j==ncols-1){ //if top right cornerpiece
if(board[i+1][j].pop) { n++; } // south
if(board[i+1][j-1].pop){ n++; } // southwest
if(board[i][j-1].pop) { n++; } // west
}
else if(i == nrows-1 && j == ncols-1){ //if bottom right cornerpiece
if(board[i][j-1].pop) { n++; } // west
if(board[i-1][j-1].pop){ n++; } // northwest
if(board[i-1][j].pop) { n++; } // north
}
else if(i == nrows-1 && j == 0){ //if bottom left cornerpiece
if(board[i-1][j].pop) { n++; } // north
if(board[i-1][j+1].pop){ n++; } // northeast
if(board[i][j+1].pop) { n++; } // east
}
else if(i == 0){ // middle top piece (not in a corner)
if(board[i][j+1].pop) { n++; } // east
if(board[i+1][j+1].pop){ n++; } // southeast
if(board[i+1][j].pop) { n++; } // south
if(board[i+1][j-1].pop){ n++; } // southwest
if(board[i][j-1].pop) { n++; } // west
}
else if(j == ncols-1){ // middle right side piece
if(board[i+1][j].pop) { n++; } // south
if(board[i+1][j-1].pop){ n++; } // southwest
if(board[i][j-1].pop) { n++; } // west
if(board[i-1][j-1].pop){ n++; } // northwest
if(board[i-1][j].pop) { n++; } // north
}
else if(i == nrows-1){ // middle bottom piece
if(board[i][j-1].pop) { n++; } // west
if(board[i-1][j-1].pop){ n++; } // northwest
if(board[i-1][j].pop) { n++; } // north
if(board[i-1][j+1].pop){ n++; } // northeast
if(board[i][j+1].pop) { n++; } // east
}
else{
if(board[i-1][j].pop) { n++; } // north
if(board[i-1][j+1].pop){ n++; } // northeast
if(board[i][j+1].pop) { n++; } // east
if(board[i+1][j+1].pop){ n++; } // southeast
if(board[i+1][j].pop) { n++; } // south
if(board[i+1][j-1].pop){ n++; } // southwest
if(board[i][j-1].pop) { n++; } // west
if(board[i-1][j-1].pop){ n++; } // northwest
}
if(status){
if(n < 2) { return 0; }
if(n >= 2 && n <= 3){ return 1; }
if(n > 3) { return 0; }
}
if(!status){
if(n == 3) { return 1; }
return 0;
}
//We should never reach here
return -1;
}
void step(struct cell ***board, int nrows, int ncols){ // returns array of strings that contain all of the 5 latest states
struct cell **nextgenboard;
nextgenboard = malloc(nrows*(sizeof(nextgenboard)) + nrows * ncols * sizeof(**nextgenboard));
struct cell * const firstrow = nextgenboard + nrows;
for(int i = 0; i < nrows; i++){
nextgenboard[i] = firstrow + i*ncols;
}
for(int i = 0; i < nrows; i++){
for(int j = 0; j < ncols; j++){
}
}
for(int r = 0; r < nrows; r++){
for(int c = 0; c < ncols; c++){
nextgenboard[r][c].pop = live(r, c, *board, nrows, ncols);
if((*board)[r][c].pop == 0){
nextgenboard[r][c].age = 0;
}else if((*board)[r][c].pop == 1){
nextgenboard[r][c].age = (*board)[r][c].age + 1;
}
//nextgenboard[r][c].age = 0;
}
}
free(*board);
*board = nextgenboard;
}
void push_list(char **list, int len, char *state){ //takes an array of strings, the length of it and a new string. Pushes the other strings down and puts the new string in the first place. The last one gets destroyed.
//move all the other elements down.
for(int i = len-1; i > 0; i--){
strcpy(list[i], list[i-1]); <---- HERE IS THE ERROR
}
//put the new element first
strcpy(list[0], state);
}
int repetitive(char **statelist, int len){ // takes the statelist and checks if the first element reoccurs. Returns 0 for no repetition, and the period otherwise.
for(int i = 1; i < len; i++){
if(strcmp(statelist[0], statelist[i]) == 0){
return i;
}
}
return 0;
}
char *serialize_board(struct cell **board, int nrows, int ncols){
char *str;
str = malloc(sizeof(char)*nrows*ncols);
int z = 0;
printf("\n");
for(int i = 0; i < nrows; i++){
for(int j = 0; j < ncols; j++){
str[z] = board[i][j].pop+48;
z++;
}
}
printf("\n");
return str;
}
EDIT1:在产生错误的地方添加了评论。我的问题是:为什么会发生这种情况,我该怎么办?
编辑 2:
在他的回答中,@Neil 说我的代码有效地做到了这一点:
char str[10];
strcpy(&str[1], &str[0]);
为了创建一个更好地模拟我的程序的示例(例如 str 是一个 char ** 而不是 char *),我写了这个:
int main(){
char **str;
str = malloc(sizeof(char *)*3 + sizeof(char)*3*10);
char * firststring = str+3;
for(int i = 0; i < 10; i++){
str[i] = firststring + i*10;
}
strcpy(str[0], "123456789");
strcpy(str[1], "000000000");
printf("%s\n", str[0]);
printf("%s\n", str[1]);
strcpy(str[1], str[0]);
printf("%s\n", str[0]);
printf("%s\n", str[1]);
}
在这里,我使用 strcpy
将一个数组元素(包含指向 char 的指针)复制到另一个数组元素。这很好用。那么,为什么我的代码不起作用?我希望它在原则上做与这个迷你示例相同的事情。
编辑 3:
所以一些更改使我的程序最终可以按照我的意愿进行。
- 修复
main()
中的错误内存分配。
- 用
'[=27=]'
. 终止 statelist
中的每个字符串
- 初始化 statelist 为
[=28=]
(虽然程序只用上面两个就可以正常执行)
1.:
char **statelist;
statelist = malloc((MAX_REPETITION_PERIOD+1) * sizeof(char *) + (MAX_REPETITION_PERIOD+1) * (nrows * ncols + 1) * sizeof(char));
char *firstrow = statelist + MAX_REPETITION_PERIOD+1;
for(int i = 0; i < (MAX_REPETITION_PERIOD+1); i++){
statelist[i] = firstrow + i * (nrows * ncols + 1);
*statelist[i] = '[=14=]';
}
2.:
char *serialize_board(struct cell **board, int nrows, int ncols){
char *str;
str = malloc(sizeof(char)*nrows*ncols);
int z = 0;
printf("\n");
for(int i = 0; i < nrows; i++){
for(int j = 0; j < ncols; j++){
str[z] = board[i][j].pop+48;
z++;
}
}
str[z] = '[=15=]'; <---- HERE
return str;
}
3.:
见 1.
这里:
void push_list(char **list, int len, char *state){ //takes an array of strings, the length of it and a new string. Pushes the other strings down and puts the new string in the first place. The last one gets destroyed.
//move all the other elements down.
for(int i = len-1; i > 0; i--){
strcpy(list[i], list[i-1]); // <-- HERE
}
//put the new element first
strcpy(list[0], state);
}
list 是指向 char 的指针。它没有固有长度(除了 char*)。编译器 不知道 list[i]
的长度超过 1 个字节。
实际上,您的代码确实:
char str[10];
strcpy(&str[1], &str[0]);
看看重叠是如何发生的?
好的,让我们试试这个。
1. char str[10] = "ABCDEFGHI";
2. strcpy(&str[1], &str[0]);
假设您有一个 'string' 的 9 个字符(+1 个 nul 终止符)- 第 1 行。
第 2 行将尝试将 "BCDEFGHI" 移动到被 "ABCDEFGHI":
占据的 space
首先,statelist
包含一堆指向未初始化内存的指针。当您尝试从这些指针之一 strcpy
时,这会导致未定义的行为。
但即使您将内存初始化为零,您所做的从根本上来说也是不正确的。您使用 strcpy
函数和其他 str*
函数,但这些函数仅适用于以零结尾的字符串。 serialize_board
返回的内存,您试图 strcpy
只是一堆 char
不是零终止的。所以你又遇到了未定义的行为。
您可以使用 memcpy
函数来复制这些内存块,因为您知道要复制的内存大小。您还需要使用 memcpy
而不是 strcmp
.
你这里也有一些严重的内存泄漏(例如 serialize_board
分配的内存没有被释放)。
这是我对康威生命游戏的实现。
我目前正在尝试实施重复检测,它允许程序检测周期长达 4 代的重复。为此,我使用了一个名为 statelist
的列表,该列表旨在存储当前状态后面的 4 个状态(状态表示为由函数 serialize_board()
创建的字符串)。为了将新状态推送到 statelist
(并可能丢弃路径中的最后一个状态),我实现了 push_list()
函数。但是在push_list()
函数中,出现错误:
2017-07-18 13:26:32.496180+0100 Assignment 3[38082:4064098] detected source and destination buffer overlap
函数初始化的注释中描述了 push_list
所需的功能。我不明白字符串如何重叠。每个字符串的statelist
和nrows*ncols+1
字节内存我都分配了足够的内存,也就是每个状态字符串的大小。对于冗长的程序感到抱歉,但是当这么多功能相互依赖时很难缩短它。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define MAX_COORDINATE_SIZE 50
#define MAX_FILENAME_SIZE 20
#define MAX_GENERATIONS 10
#define MAX_REPETITION_PERIOD 4
struct coord{ //Holds coordinates to a cell
int x;
int y;
};
struct cell{
int pop; //Populated
// char pos; //C: corner, T: top, R: right, B: bottom, L: left
int age;
};
struct coord *read_init(FILE *fp, int *i);
static int read_line(FILE *fp, char *line, int max_length);
struct coord read_coords(char *line);
struct cell **create_board(int x, int y);
void populate_board(struct coord *coords, struct cell ***board, int *n);
struct cell new_cell(int x, int y, int pop, int age);
struct cell **start_game(FILE *fp, int nrows, int ncols);
void print_board(struct cell **board, int nrows, int ncols);
void init_board(int nrows, int ncols, struct cell ***board);
int live(int i, int j, struct cell **board, int nrows, int ncols);
void step(struct cell ***board, int nrows, int ncols);
void push_list(char **list, int len, char *state);
int repetitive(char **list, int len);
char *serialize_board(struct cell **board, int nrows, int ncols);
int main(int argc, const char * argv[]) {
int gens;
char gens_string[MAX_GENERATIONS];
if(argc != 3){
fprintf(stderr, "Usage: %s <seed-file> <generations>\n<seed-file> can me up to %d characters long\n", argv[0], MAX_FILENAME_SIZE);
exit(1);
}
FILE *fp = fopen(argv[1], "r");
strncat(gens_string, argv[2], MAX_GENERATIONS);
gens = atoi(gens_string);
int nrows = 10;
int ncols = 10;
int repetitions;
//allocate memory and set up pointers for statelist
char **statelist;
statelist = malloc((MAX_REPETITION_PERIOD+1) * sizeof(char *) + (MAX_REPETITION_PERIOD+1+1) * nrows * ncols); //MAXREP+1 is the number of strings we want to store. The other +1 is to fit in the null temrinator
char *firstrow = statelist + MAX_REPETITION_PERIOD+1;
for(int i = 0; i < MAX_REPETITION_PERIOD+1; i++){
statelist[i] = firstrow + i * nrows * ncols;
}
struct cell **board= start_game(fp, nrows, ncols);
print_board(board, nrows, ncols);
push_list(statelist, MAX_REPETITION_PERIOD+1, serialize_board(board, nrows, ncols));
struct timespec t;
t.tv_sec = 0;
t.tv_nsec = 500000000L;
for(int i = 0; i < gens; i++){
step(&board, nrows, ncols);
nanosleep(&t, NULL);
print_board(board, nrows, ncols);
push_list(statelist, MAX_REPETITION_PERIOD+1, serialize_board(board, nrows, ncols));
if((repetitions = repetitive(statelist, MAX_REPETITION_PERIOD+1))){
printf("Repetition detected (%d)\n", repetitions);
exit(1);
}
}
return 0;
}
struct coord *read_init(FILE *fp, int *n){ //Takes in filename and returns list of coordinates to be populated
char raw_n[100];
struct coord *coords;
char *line;
line = malloc(sizeof(char)*MAX_COORDINATE_SIZE);
read_line(fp, raw_n, 100); // get the first line of the file (number of popuated cells)
*n = atoi(raw_n);//make an int out of raw_n
coords = malloc(sizeof(struct coord)*(*n)); //Allocate memory for each coord
for(int i = 0; i<(*n); i++){ // for each line in the file (each populated cell)
read_line(fp, line, MAX_COORDINATE_SIZE);
coords[i] = read_coords(line); //Put coordinates in coords
line[0] = '[=11=]';
}
return coords; // return coordinates
}
static int read_line ( FILE *fp, char *line, int max_length)
{
int i;
char ch;
/* initialize index to string character */
i = 0;
/* read to end of line, filling in characters in string up to its
maximum length, and ignoring the rest, if any */
for(;;)
{
/* read next character */
ch = fgetc(fp);
/* check for end of file error */
if ( ch == EOF )
return -1;
/* check for end of line */
if ( ch == '\n' )
{
/* terminate string and return */
line[i] = '[=11=]';
return 0;
}
/* fill character in string if it is not already full*/
if ( i < max_length )
line[i++] = ch;
}
/* the program should never reach here */
return -1;
}
struct coord read_coords(char *line){ // Returns coordinates read from char *line
struct coord c;
char *x;
char *y;
x = malloc(sizeof(char)*MAX_COORDINATE_SIZE);
y = malloc(sizeof(char)*MAX_COORDINATE_SIZE);
int i = 0;
do{
x[i] = line[i]; //Get the x coordinate
i++;
}while(line[i] != ' ');
i++;
do{
y[i-2] = line[i];
i++;
}while(line[i] != '[=11=]');
c.x = atoi(x)-1;
c.y = atoi(y)-1;
return c;
}
void init_board(int nrows, int ncols, struct cell ***board){
*board = malloc(nrows * sizeof(*board) + nrows * ncols * sizeof(**board));
//Now set the address of each row or whatever Whosebug says
struct cell * const firstrow = *board + nrows;
for(int i = 0; i < nrows; i++)
{
(*board)[i] = firstrow + i * ncols;
}
for(int i = 0; i < nrows; i++){ //fill the entire board with pieces
for(int j = 0; j < ncols; j++){
(*board)[i][j] = new_cell(i, j, 0, 0);
}
}
}
struct cell new_cell(int x, int y, int pop, int age){ //Return new populated or non-populated cell with specified coordinates
struct cell c;
c.pop = pop;
c.age = age;
return c;
}
struct cell **start_game(FILE *fp, int nrows, int ncols){ //x,y are no of rows/columns, fn is filename
int n; // n is the number of populated cells specified in the seed
struct coord *coords = read_init(fp, &n); // get the list of coords to populate board with
struct cell **board;
init_board(nrows, ncols, &board); // Set up the board
populate_board(coords, &board, &n); //populate the cells specified in the seed
return board;
}
void populate_board(struct coord *coords, struct cell ***board, int *n){
for(int i = 0; i < *n; i++){
(*board)[coords[i].x][coords[i].y].pop = 1; //populate the cell
}
}
void print_board(struct cell **board, int nrows, int ncols){
printf("--------------------\n");
for(int i = 0; i<nrows; i++){
for(int j = 0; j<ncols; j++){
if(board[i][j].pop == 1){
printf("%d ", board[i][j].age);
}else if(board[i][j].pop == 0){
printf(" ");
}else{
printf("\n\nERROR!");
exit(0);
}
}
printf("\n");
}
printf("--------------------");
printf("\n");
}
int live(int i, int j, struct cell **board, int nrows, int ncols){ //returns 1 if cell will live, 0 if cell will die,
int status = board[i][j].pop; // status = 1 if alive, 0 if dead
int n=0; //neighbours
//Counting all the neighbours
if(i == 0 && j == 0){ //if top left cornerpiece
if(board[i][j+1].pop) { n++; } // east
if(board[i+1][j+1].pop){ n++; } // southeast
if(board[i+1][j].pop) { n++; } // south
}
else if(i == 0 && j==ncols-1){ //if top right cornerpiece
if(board[i+1][j].pop) { n++; } // south
if(board[i+1][j-1].pop){ n++; } // southwest
if(board[i][j-1].pop) { n++; } // west
}
else if(i == nrows-1 && j == ncols-1){ //if bottom right cornerpiece
if(board[i][j-1].pop) { n++; } // west
if(board[i-1][j-1].pop){ n++; } // northwest
if(board[i-1][j].pop) { n++; } // north
}
else if(i == nrows-1 && j == 0){ //if bottom left cornerpiece
if(board[i-1][j].pop) { n++; } // north
if(board[i-1][j+1].pop){ n++; } // northeast
if(board[i][j+1].pop) { n++; } // east
}
else if(i == 0){ // middle top piece (not in a corner)
if(board[i][j+1].pop) { n++; } // east
if(board[i+1][j+1].pop){ n++; } // southeast
if(board[i+1][j].pop) { n++; } // south
if(board[i+1][j-1].pop){ n++; } // southwest
if(board[i][j-1].pop) { n++; } // west
}
else if(j == ncols-1){ // middle right side piece
if(board[i+1][j].pop) { n++; } // south
if(board[i+1][j-1].pop){ n++; } // southwest
if(board[i][j-1].pop) { n++; } // west
if(board[i-1][j-1].pop){ n++; } // northwest
if(board[i-1][j].pop) { n++; } // north
}
else if(i == nrows-1){ // middle bottom piece
if(board[i][j-1].pop) { n++; } // west
if(board[i-1][j-1].pop){ n++; } // northwest
if(board[i-1][j].pop) { n++; } // north
if(board[i-1][j+1].pop){ n++; } // northeast
if(board[i][j+1].pop) { n++; } // east
}
else{
if(board[i-1][j].pop) { n++; } // north
if(board[i-1][j+1].pop){ n++; } // northeast
if(board[i][j+1].pop) { n++; } // east
if(board[i+1][j+1].pop){ n++; } // southeast
if(board[i+1][j].pop) { n++; } // south
if(board[i+1][j-1].pop){ n++; } // southwest
if(board[i][j-1].pop) { n++; } // west
if(board[i-1][j-1].pop){ n++; } // northwest
}
if(status){
if(n < 2) { return 0; }
if(n >= 2 && n <= 3){ return 1; }
if(n > 3) { return 0; }
}
if(!status){
if(n == 3) { return 1; }
return 0;
}
//We should never reach here
return -1;
}
void step(struct cell ***board, int nrows, int ncols){ // returns array of strings that contain all of the 5 latest states
struct cell **nextgenboard;
nextgenboard = malloc(nrows*(sizeof(nextgenboard)) + nrows * ncols * sizeof(**nextgenboard));
struct cell * const firstrow = nextgenboard + nrows;
for(int i = 0; i < nrows; i++){
nextgenboard[i] = firstrow + i*ncols;
}
for(int i = 0; i < nrows; i++){
for(int j = 0; j < ncols; j++){
}
}
for(int r = 0; r < nrows; r++){
for(int c = 0; c < ncols; c++){
nextgenboard[r][c].pop = live(r, c, *board, nrows, ncols);
if((*board)[r][c].pop == 0){
nextgenboard[r][c].age = 0;
}else if((*board)[r][c].pop == 1){
nextgenboard[r][c].age = (*board)[r][c].age + 1;
}
//nextgenboard[r][c].age = 0;
}
}
free(*board);
*board = nextgenboard;
}
void push_list(char **list, int len, char *state){ //takes an array of strings, the length of it and a new string. Pushes the other strings down and puts the new string in the first place. The last one gets destroyed.
//move all the other elements down.
for(int i = len-1; i > 0; i--){
strcpy(list[i], list[i-1]); <---- HERE IS THE ERROR
}
//put the new element first
strcpy(list[0], state);
}
int repetitive(char **statelist, int len){ // takes the statelist and checks if the first element reoccurs. Returns 0 for no repetition, and the period otherwise.
for(int i = 1; i < len; i++){
if(strcmp(statelist[0], statelist[i]) == 0){
return i;
}
}
return 0;
}
char *serialize_board(struct cell **board, int nrows, int ncols){
char *str;
str = malloc(sizeof(char)*nrows*ncols);
int z = 0;
printf("\n");
for(int i = 0; i < nrows; i++){
for(int j = 0; j < ncols; j++){
str[z] = board[i][j].pop+48;
z++;
}
}
printf("\n");
return str;
}
EDIT1:在产生错误的地方添加了评论。我的问题是:为什么会发生这种情况,我该怎么办?
编辑 2:
在他的回答中,@Neil 说我的代码有效地做到了这一点:
char str[10];
strcpy(&str[1], &str[0]);
为了创建一个更好地模拟我的程序的示例(例如 str 是一个 char ** 而不是 char *),我写了这个:
int main(){
char **str;
str = malloc(sizeof(char *)*3 + sizeof(char)*3*10);
char * firststring = str+3;
for(int i = 0; i < 10; i++){
str[i] = firststring + i*10;
}
strcpy(str[0], "123456789");
strcpy(str[1], "000000000");
printf("%s\n", str[0]);
printf("%s\n", str[1]);
strcpy(str[1], str[0]);
printf("%s\n", str[0]);
printf("%s\n", str[1]);
}
在这里,我使用 strcpy
将一个数组元素(包含指向 char 的指针)复制到另一个数组元素。这很好用。那么,为什么我的代码不起作用?我希望它在原则上做与这个迷你示例相同的事情。
编辑 3:
所以一些更改使我的程序最终可以按照我的意愿进行。
- 修复
main()
中的错误内存分配。 - 用
'[=27=]'
. 终止 - 初始化 statelist 为
[=28=]
(虽然程序只用上面两个就可以正常执行)
statelist
中的每个字符串
1.:
char **statelist;
statelist = malloc((MAX_REPETITION_PERIOD+1) * sizeof(char *) + (MAX_REPETITION_PERIOD+1) * (nrows * ncols + 1) * sizeof(char));
char *firstrow = statelist + MAX_REPETITION_PERIOD+1;
for(int i = 0; i < (MAX_REPETITION_PERIOD+1); i++){
statelist[i] = firstrow + i * (nrows * ncols + 1);
*statelist[i] = '[=14=]';
}
2.:
char *serialize_board(struct cell **board, int nrows, int ncols){
char *str;
str = malloc(sizeof(char)*nrows*ncols);
int z = 0;
printf("\n");
for(int i = 0; i < nrows; i++){
for(int j = 0; j < ncols; j++){
str[z] = board[i][j].pop+48;
z++;
}
}
str[z] = '[=15=]'; <---- HERE
return str;
}
3.:
见 1.
这里:
void push_list(char **list, int len, char *state){ //takes an array of strings, the length of it and a new string. Pushes the other strings down and puts the new string in the first place. The last one gets destroyed.
//move all the other elements down.
for(int i = len-1; i > 0; i--){
strcpy(list[i], list[i-1]); // <-- HERE
}
//put the new element first
strcpy(list[0], state);
}
list 是指向 char 的指针。它没有固有长度(除了 char*)。编译器 不知道 list[i]
的长度超过 1 个字节。
实际上,您的代码确实:
char str[10];
strcpy(&str[1], &str[0]);
看看重叠是如何发生的?
好的,让我们试试这个。
1. char str[10] = "ABCDEFGHI";
2. strcpy(&str[1], &str[0]);
假设您有一个 'string' 的 9 个字符(+1 个 nul 终止符)- 第 1 行。 第 2 行将尝试将 "BCDEFGHI" 移动到被 "ABCDEFGHI":
占据的 space首先,statelist
包含一堆指向未初始化内存的指针。当您尝试从这些指针之一 strcpy
时,这会导致未定义的行为。
但即使您将内存初始化为零,您所做的从根本上来说也是不正确的。您使用 strcpy
函数和其他 str*
函数,但这些函数仅适用于以零结尾的字符串。 serialize_board
返回的内存,您试图 strcpy
只是一堆 char
不是零终止的。所以你又遇到了未定义的行为。
您可以使用 memcpy
函数来复制这些内存块,因为您知道要复制的内存大小。您还需要使用 memcpy
而不是 strcmp
.
你这里也有一些严重的内存泄漏(例如 serialize_board
分配的内存没有被释放)。