【问题标题】:Conway's Game of Life Buffer Underflow康威的生命游戏缓冲下溢
【发布时间】:2019-03-12 08:39:36
【问题描述】:

我对 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;
}

【问题讨论】:

    标签: c conways-game-of-life stackunderflow


    【解决方案1】:

    ij 为零时,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可以访问未映射的内存吗?为什么 C 不针对我尝试测试的所有情况给出错误?
    • @Manav 从不可以超出数组的范围。另一种方法是使用模数运算进行索引,以便板“包裹”到另一边:环形板。所以original[(i-1+numRows) % numRows] [(j-1+numCols) % numCols].
    • @Manav:C 可以让您访问数组外部。它可能不会给你一个错误,但那是因为它没有捕捉到你。 C 标准不需要针对违反规则的内置保护,并且几乎没有 C 实现具有通过检查。就像我们有法律一样,但不是到处都有警察。有一些检查,所以有时你会被发现违反规则。但其他时候,你不会被抓到,后果可能会有所不同。
    • @EricPostpischil 感谢您提供简洁易懂的解释! :)
    猜你喜欢
    • 2013-02-21
    • 1970-01-01
    • 2010-09-07
    • 2017-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多