arrays - 生成伪语言的 C 程序 - 全局 3D 数组太大(段错误)?

标签 arrays c multidimensional-array segmentation-fault heap-memory

我应该编写一个程序,通过解析现有的英语文本并查看打印的最后两个字母来确定下一个可能是什么(第一个被想象为“.”),以伪英语打印文本。 ' 和 ' ')。对于该任务,我想出了以下代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

short characters[256][256][256];

int main(int argc, char* argv[]){   
    if(argc<2){
        printf("In addition to the input file and maybe output file, please enter the number of output sentences as a command line argument.\n");
        return 1;
        }

    /*Different approach where I malloced the array instead, same result*/
    /*short ***characters=malloc(256 * sizeof(short**));
    for(int i=0; i<256; i++){
        *characters[i]=malloc(256 * sizeof(short*));
        for(int i2=0; i2<256; i++){
            characters[i][i2]=malloc(256 * sizeof(short**));
            }
        }*/

    /*Read text*/
    char a='.', /*pre-previous character*/
    b=' ', /*previous character*/
    c; /*current character*/
    int n=0;
    while((c=getchar())!=EOF){
        characters[a][b][c]++;
        a=b;
        b=c;
        n++;
        }

    /*Check how many sentences should be printed*/
    int sentences=0, multiplier=1;
    for(int i=0; i<sizeof(argv[1])/8; i++){
        sentences+=argv[1][i]*multiplier;
        multiplier*=10;
        }

    /*Print text*/
    int currentsentences=0, random, p1, p2;
    a='.';
    b=' ';
    while(currentsentences<sentences){
        int uninitialized;
        srand(time(0)+p1+p2+uninitialized); /*adds a bit of entropy*/
        random=rand()%n;
        p1=0;
        for(int i=0; ; i++){
            p2=p1+characters[a][b][i];
            if(random>p1 && random<=p2){
                c=characters[a][b][i];
                p1+=characters[a][b][i];
                break;
                }
            }
        putchar(c);
        if(c=='.' || c=='?' || c=='!')
            currentsentences++;
        a=b;
        b=c;
        }

    return 0;
    }

它编译时没有错误或警告,但是,当我尝试运行该程序时,它总是在打印任何内容之前返回段错误,除非我没有输入足够的命令行参数,在这种情况下它会输入第一个 if 子句。这就是为什么我认为它必须对 3D 数组做一些事情,因为它似乎甚至无法进入第一个循环(如果我让它在此之前打印一些东西,它就不会)。它需要那么大,因为结构如下:[前一个字母][前一个字母][当前字母]=这个星座出现的频率。因为我可能不需要更高的 ASCII 并且 char 的范围可能就足够了,所以我尝试使用 char 而不是 short 和一个数组128*128*128 - 相同的结果。以 root 身份运行并没有太大变化,增加 ulimit 也是如此。然而,全局变量不是保存在堆中的吗?我上面注释掉的 malloc() 的使用也没有改变任何东西。我在两台机器上尝试过这一点,一台操作系统:X、64 位和 8GB DDR3,另一台操作系统是 Linux Mint 19.1、64 位和 32GB DDR4。两者的结果再次相同(MacOS 表示段错误:11,Linux 表示段错误(核心转储))。由于该阵列的已用内存约为 33 MB,因此我的 RAM 也不是问题。那么为什么会出现段错误呢?我是否需要为堆分配更多的 RAM(我认为这是不可能的)?这可能与数组和/或其大小无关吗?

这是该程序的最新版本;仍然表现出相同的行为:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

short characters[256][256][256];

int main(int argc, char* argv[]){   

    /*Check if number of sentences was given*/
    if(argc<2){
        printf("In addition to the input file and maybe output file, please enter the number of output sentences as a command line argument.\n");
        return 1;
        }

    /*Different approach with malloc*/
    /*short ***characters=malloc(256 * sizeof(short**));
    for(int i=0; i<256; i++){
        *characters[i]=malloc(256 * sizeof(short*));
        for(int i2=0; i2<256; i++){
            characters[i][i2]=malloc(256 * sizeof(short**));
            }
        }*/

    /*Read input text*/
    int a='.', /*pre-previous character*/
    b=' ', /*previous character*/
    c; /*current character*/
    int n=0;
    for(; (c=getchar())!=EOF; n++){
        characters[a][b][c]++;
        a=b;
        b=c;
        }

    /*Check how many sentences should be printed*/
    int sentences=0, multiplier=1;
    for(int i=strlen(argv[1])-1; i>=0; i--){
        sentences+=(argv[1][i]-'0')*multiplier;
        multiplier*=10;
        }

    /*Print text*/
    int currentsentences=0, random, p1=0, p2=0;
    a='.';
    b=' ';
    srand(time(0));
    while(currentsentences<sentences){
        random=(rand()+p1+p2)%n;
        p1=0;
        for(int i=0; i<256; i++){
            p2=p1+characters[a][b][i]; /*Determine range for character*/
            if(random>p1 && random<=p2){ /*Cheack if random number is in range of character*/
                c=characters[a][b][i];
                p1+=characters[a][b][i];
                break;
                }
            }
        putchar(c);
        if(c=='.' || c=='?' || c=='!')
            currentsentences++;
        a=b;
        b=c;
        }

    return 0;
    }

更新:它显示的一个有趣的行为是,如果您在程序的最开头添加类似 printf(„here“) 的内容,它将输出 „here “ if 第一个 if 语句如果输入。但是,如果不是,程序将在打印任何内容之前返回段错误。

更新2:有趣的是,如果您不提供输入文件并手动输入所有内容,它不会返回段错误,但也永远不会完成。

更新 3:该程序现在可以运行,请参见下文。对于我造成的所有问题,我们深表歉意,并感谢您对我的帮助。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

unsigned int characters[128][128][128];

int main(int argc, char* argv[]){   
     /*Check if input file was given*/
    if(argc<2){
        printf("Please enter an input file as command line argument.\n");
        return 1;
            }

    /*Check for input file, open it*/
    FILE *fp=NULL;
    fp=fopen(argv[1], "r");
    if(!fp){
        printf("Error 404: Input file not found.\n");
        return 404;
        }

    /*Read input text*/
    int a='.';  /*pre-previous character*/
    int b=' ';  /*previous character*/
    int c;      /*current character*/

    while((c=fgetc(fp))!=EOF){
        if(c<127 && c>='\t'){ /*All characters from higher ASCII and system codes ignored. Still uses letters, digits and typical special characters and formatting characters.*/ 
            characters[a][b][c]++;
            a=b;
            b=c;
            }
        }
    fclose(fp);

    /*Check how many sentences should be printed*/
    unsigned int sentences;
    printf("How many sentences do you want to be printed? ");
    scanf("%d", &sentences);

    /*Print text*/
    unsigned int currentsentences=0, random, p1=0, p2=0, n;
    a='.';
    b=' ';
    srand(time(0));
    while(currentsentences<sentences){
        n=0;
        for(int i='\t'; i<127; i++){
            n+=characters[a][b][i];
            }
        random=(rand()+p1+p2+sentences+currentsentences+clock())%n;
        p1=0;
        for(int i='\t'; i<127; i++){    
            p2=p1+characters[a][b][i]; /*Determine range for character in combination with line 58*/
            if(random>=p1 && random<p2 && characters[a][b][i]!=0){ /*Check if random number is in range of character and that character occured in that combination*/
                c=i;
                printf("%c", c);
                characters[a][b][c]++; /*Experimental, language will change over time pseudo-randomly*/
                break;
                }
            p1+=characters[a][b][i];
            }
        if(c=='.' || c=='?' || c=='!')
            currentsentences++;
        a=b;
        b=c;
        }

    printf("\n");

    return 0;
    }

最佳答案

主要问题在这部分代码:

    p1=0;
    for(int i=0; ; i++){
        p2=p1+characters[a][b][i];
        if(random>p1 && random<=p2){
            c=characters[a][b][i];
            p1+=characters[a][b][i];
            break;
        }
    }

在这里,您不断增加i,而不检查越界访问。你应该有这样的东西:

if (i >= 255) { // error handling ....};

另请注意,循环中的 p1 始终为零。

在这一部分

random=(rand()+p1+p2)%n;

p1p2 未初始化,因此您最终可能会得到一个负数,这显然意味着您从未命中过break语句。换句话说 - 一个无限循环,您不断增加 i (这会导致越界访问)。

作为示例,我更改了代码,如下所示:

    for(int i=0; ; i++){
        printf("random=%d p1=%d a=%c b=%c i=%d", random, p1, a, b, i);

并得到如下输出:

...
random=-3 p1=0 a=. b=  i=42484 p2=0
random=-3 p1=0 a=. b=  i=42485 p2=0
random=-3 p1=0 a=. b=  i=42486 p2=0
random=-3 p1=0 a=. b=  i=42487 p2=0
...

请注意,random 为负数,因此循环永远不会终止。

关于arrays - 生成伪语言的 C 程序 - 全局 3D 数组太大(段错误)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56113345/

相关文章:

c++ - 我可以在 C++ 中有一个 ifstream 数组吗

c 正则表达式找不到字符串

PHP - 替换多维数组中的数据,特定键

c - long long int 初始化警告

c - 使用未声明的标识符 'a'

Java - 使用递归的深度克隆数组;未知类型和未知深度

PHP 7 - 如何搜索数组的多个特定成员

JavaScript:如何使 Var "Array"工作?

arrays - 在 WHERE IN 子句中使用来自 JSONB 数组的值

arrays - 如何在 Pascal 或任何编程语言中实现字幕文本?