康威的生命游戏缓冲区下溢
Conway's Game of Life Buffer Underflow
我是 C 的新手,之前听说过缓冲区溢出,但从未听说过堆栈缓冲区下溢。我一直在尝试阅读它,据我了解,我分配了太多内存?我只是想确保我正确理解了这个问题。所以我的问题与以下代码有关,该代码需要几代才能更新康威生命游戏的给定文件。如果有人能解释我在哪里误解了什么,我将不胜感激。输入应遵循“./life.c # board.txt”,其中# 是代数,board.txt 是由“.”和“*”构成的棋盘的。 board.txt 的第一行还包含行数和列数。奇怪的是,该代码有时适用于较小的电路板,但会为较大的电路板创建缓冲区下溢。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void futureGens(int numRows, int numCols, int original[numRows][numCols], int generations){
int future[numRows][numCols];
int i, j;
for(i = 0; i < numRows; i++){
for(j = 0; j < numCols; j++){
int live = 0;
if(original[i-1][j-1] == 1){
live++;
}
if(original[i-1][j] == 1){
live++;
}
if(original[i-1][j+1] == 1){
live++;
}
if(original[i][j-1] == 1){
live++;
}
if(original[i][j] == 1){
live++;
}
if(original[i][j+1] == 1){
live++;
}
if(original[i+1][j-1] == 1){
live++;
}
if(original[i+1][j] == 1){
live++;
}
if(original[i+1][j+1] == 1){
live++;
}
live -= original[i][j];
switch(live){
case 0:
case 1:
future[i][j] = 0;
break;
case 2:
future[i][j] = original[i][j];
break;
case 3:
future[i][j] = 1;
break;
default:
future[i][j] = 0;
}
}
}
if(generations == 1){
//printf("\nFuture: \n");
for(i = 0; i < numRows; i++){
for(j = 0; j < numCols; j++){
if(future[i][j] == 1){
printf("*");
} else {
printf(".");
}
//printf("%d", future[i][j]);
}
printf("\n");
}
}
else {
futureGens(numRows, numCols, future, generations-1);
}
}
int main(int argc, char **argv){
if(argc != 3) {
return EXIT_FAILURE;
}
int generations = atoi(argv[1]);
FILE *fp = fopen(argv[2], "r");
if(fp == NULL){
printf("error: nothing in file\n");
return EXIT_FAILURE;
}
int numRows = 0, numCols = 0;
char line[256];
if(fgets(line, sizeof(line), fp)){
char c[256];
int p;
for(p = 0; p < 256; p++){
if(isdigit(line[p]) != 0){
c[p] = line[p];
} else {
break;
}
}
numRows = atoi(c);
numCols = atoi(c);
printf("row: %d, col: %d\n", numRows, numCols);
}
//initialize the original array
int original[numRows][numCols];
int i, j;
for(i = 0; i < numRows; i++){
fgets(line, sizeof(line), fp);
for(j = 0; j < numCols; j++){
char c = line[j];
if(c == '.'){
original[i][j] = 0;
} else if(c == '*'){
original[i][j] = 1;
}
}
}
futureGens(numRows, numCols, original, generations);
return EXIT_SUCCESS;
}
当 i
或 j
为零时,original[i-1][j-1]
会尝试访问数组 original
.
之外的元素
C 标准没有定义结果行为。在许多 C 实现中,这通常会尝试访问数组外的内存。数组行越大(列越多),数组original[i-1]
越往外,越有可能尝试访问未映射的内存,从而导致错误。
您必须编写不会访问数组外部元素的代码。
对于必须检查数组元素的邻居的算法,有一些常见的方法:
- 在考虑每个元素时,使用
if
语句来测试它是否在数组内有邻居。对于不存在邻居的任何方向,不要尝试检查那里的元素。此代码导致对每个元素重复测试数组边界。
- 将数组的处理分成内部元素的主循环(或一组嵌套循环,每个维度一个),所有这些元素在所有方向上都有邻居,并为数组的边缘单独循环(例如“左”边,其中
i
为零,并且元素在左侧没有邻居。那么不需要对每个元素进行单独测试;每个循环处理在该循环中处理的元素的邻居的情况循环是已知的。角也必须单独处理。
- 在包含中性信息的边缘用虚拟行和列填充数组。因此,对于
R
行和 C
列的所需数组大小,将使用 R+2
行和 C+2
列的实际数组大小。处理元素的循环将遍历第 1 行到 R-2
并遍历第 1 列到 C-2
.
我是 C 的新手,之前听说过缓冲区溢出,但从未听说过堆栈缓冲区下溢。我一直在尝试阅读它,据我了解,我分配了太多内存?我只是想确保我正确理解了这个问题。所以我的问题与以下代码有关,该代码需要几代才能更新康威生命游戏的给定文件。如果有人能解释我在哪里误解了什么,我将不胜感激。输入应遵循“./life.c # board.txt”,其中# 是代数,board.txt 是由“.”和“*”构成的棋盘的。 board.txt 的第一行还包含行数和列数。奇怪的是,该代码有时适用于较小的电路板,但会为较大的电路板创建缓冲区下溢。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void futureGens(int numRows, int numCols, int original[numRows][numCols], int generations){
int future[numRows][numCols];
int i, j;
for(i = 0; i < numRows; i++){
for(j = 0; j < numCols; j++){
int live = 0;
if(original[i-1][j-1] == 1){
live++;
}
if(original[i-1][j] == 1){
live++;
}
if(original[i-1][j+1] == 1){
live++;
}
if(original[i][j-1] == 1){
live++;
}
if(original[i][j] == 1){
live++;
}
if(original[i][j+1] == 1){
live++;
}
if(original[i+1][j-1] == 1){
live++;
}
if(original[i+1][j] == 1){
live++;
}
if(original[i+1][j+1] == 1){
live++;
}
live -= original[i][j];
switch(live){
case 0:
case 1:
future[i][j] = 0;
break;
case 2:
future[i][j] = original[i][j];
break;
case 3:
future[i][j] = 1;
break;
default:
future[i][j] = 0;
}
}
}
if(generations == 1){
//printf("\nFuture: \n");
for(i = 0; i < numRows; i++){
for(j = 0; j < numCols; j++){
if(future[i][j] == 1){
printf("*");
} else {
printf(".");
}
//printf("%d", future[i][j]);
}
printf("\n");
}
}
else {
futureGens(numRows, numCols, future, generations-1);
}
}
int main(int argc, char **argv){
if(argc != 3) {
return EXIT_FAILURE;
}
int generations = atoi(argv[1]);
FILE *fp = fopen(argv[2], "r");
if(fp == NULL){
printf("error: nothing in file\n");
return EXIT_FAILURE;
}
int numRows = 0, numCols = 0;
char line[256];
if(fgets(line, sizeof(line), fp)){
char c[256];
int p;
for(p = 0; p < 256; p++){
if(isdigit(line[p]) != 0){
c[p] = line[p];
} else {
break;
}
}
numRows = atoi(c);
numCols = atoi(c);
printf("row: %d, col: %d\n", numRows, numCols);
}
//initialize the original array
int original[numRows][numCols];
int i, j;
for(i = 0; i < numRows; i++){
fgets(line, sizeof(line), fp);
for(j = 0; j < numCols; j++){
char c = line[j];
if(c == '.'){
original[i][j] = 0;
} else if(c == '*'){
original[i][j] = 1;
}
}
}
futureGens(numRows, numCols, original, generations);
return EXIT_SUCCESS;
}
当 i
或 j
为零时,original[i-1][j-1]
会尝试访问数组 original
.
C 标准没有定义结果行为。在许多 C 实现中,这通常会尝试访问数组外的内存。数组行越大(列越多),数组original[i-1]
越往外,越有可能尝试访问未映射的内存,从而导致错误。
您必须编写不会访问数组外部元素的代码。
对于必须检查数组元素的邻居的算法,有一些常见的方法:
- 在考虑每个元素时,使用
if
语句来测试它是否在数组内有邻居。对于不存在邻居的任何方向,不要尝试检查那里的元素。此代码导致对每个元素重复测试数组边界。 - 将数组的处理分成内部元素的主循环(或一组嵌套循环,每个维度一个),所有这些元素在所有方向上都有邻居,并为数组的边缘单独循环(例如“左”边,其中
i
为零,并且元素在左侧没有邻居。那么不需要对每个元素进行单独测试;每个循环处理在该循环中处理的元素的邻居的情况循环是已知的。角也必须单独处理。 - 在包含中性信息的边缘用虚拟行和列填充数组。因此,对于
R
行和C
列的所需数组大小,将使用R+2
行和C+2
列的实际数组大小。处理元素的循环将遍历第 1 行到R-2
并遍历第 1 列到C-2
.