C программа для генерации псевдоязыка - глобальный 3D массив слишком велик (ошибка сегментации)? - PullRequest
0 голосов
/ 13 мая 2019

Я должен написать программу, которая печатает текст на псевдоанглийском языке, анализируя существующий текст на английском языке и просматривая последние две напечатанные буквы, чтобы определить, какой из них, вероятно, будет следующая (первая, которая представляется как '.' а также ' ').Для этой задачи я придумал следующий код:

#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;
    }

Он компилируется без ошибок и предупреждений, однако, когда я пытаюсь запустить эту программу, он всегда возвращает ошибку segfault перед печатью чего-либо, если я не делаю этого.недостаточно ввести достаточное количество аргументов командной строки, и в этом случае оно входит в первое предложение if.Вот почему я думаю, что он должен что-то делать с 3D-массивом, так как кажется, что он не может даже войти в первый цикл (если я позволю ему напечатать что-то до этого, это не будет).Он должен быть таким большим, так как структура выглядит следующим образом: [pre-previous letter][previous letter][current letter]=how often did this constellation occur.Поскольку мне, вероятно, не понадобится более высокий ASCII и диапазона char, вероятно, было бы достаточно, я попытался char вместо short и массив 128 * 128 * 128 - тот же результат.Запуск его от имени root не сильно изменился, и то же самое касается увеличения ulimit.Однако не сохраняются ли глобальные переменные в куче?Использование malloc(), которое я прокомментировал выше, также ничего не изменило.Я пробовал это на двух машинах, одна ОС: X, 64-битная и 8 ГБ DDR3, другая Linux Mint 19.1, 64-битная и 32 ГБ DDR4.И тот же результат, опять же (MacOS сказал segmentation fault: 11, Linux сказал segmentation fault (core dumped)).Поскольку используемая память этого массива составляет около 33 МБ, моя оперативная память также не может быть проблемой.Так почему же есть сегфо?Нужно ли выделять больше оперативной памяти для кучи (я не думаю, что это вообще возможно)?Может быть, это что-то, что не имеет отношения к массиву и / или его размеру?

Это последняя версия программы;все еще показывает то же поведение:

#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 оператор, если введен.Однако, если это не так, программа возвратит ошибку segf перед печатью чего-либо.

ОБНОВЛЕНИЕ 2: Интересно, что если вы не дадите входной файл и введете все вручную, она не вернет ошибку segfault, но такженикогда не заканчивай так же.

ОБНОВЛЕНИЕ 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;
    }

Ответы [ 3 ]

4 голосов
/ 13 мая 2019

Предупреждения, ошибки и некоторые очень хорошие предложения указаны в комментариях под вашим постом. nota bene .

Следующее утверждение комментария кажется достаточно ясным,

/*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;
        }

Таким образом, следующий короткий фрагмент является предложением для другого подхода:

// assume at minimum input of one legal filespec,  
// eg: .\\filename.txt (Windows) or ./filename.txt (Linux)
int main(int argc, char *argv[])
{
    FILE *fp = NULL;
    int c = 0;
    int sentences = 0;

    if(argc<2)
    {
        printf("Minimum command line usage:  <name>.exe [pathFileName].  Program exiting.");
        getchar();
        return 0;
    }

    fp = fopen(argv[1], "r");
    if(fp)
    {
        c = fgetc(fp); 
        while(c) // will exit upon EOF (-1) Note c is int, not char
        {
            if( (c=='.') || (c=='?') || (c=='!') )
            {
                sentences++;
            }
        }
        fclose(fp);
    }
    else return 0;  //error, file not opened.

    /* rest of your code here */

    return 0;
}
4 голосов
/ 13 мая 2019

Основная проблема в этой части кода:

    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;

p1 и p2 неинициализированы, поэтому вы можете получить отрицательное число, что, очевидно, означает, что вы никогда не нажимаете оператор 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 отрицательно, поэтому цикл никогда не может завершиться.

3 голосов
/ 13 мая 2019

Вся логика выбора следующего символа неверна:

  • После цикла, повторяющего i для проверки characters[a][b][i], код отправляет c на вывод. В этот момент значение c либо осталось от некоторого предыдущего кода, либо characters[a][b][i] для некоторого i, что означает, что это число троек, которые были замечены во время анализа, - это не код для символа, который должен быть напечатанным.
  • Код для подготовки p1 и p2 и сравнения их со случайным числом не имеет смысла. Код должен выбрать случайное число в [0, N ), где N - сумма characters[a][b][i] для всех кодов символов i, а затем выбрать код символа c так, что c находится в [p1, p2), где p1 - сумма characters[a][b][i] для 0 ≤ i <<code>c, а p2 - p1 + characters[a][b][c].
...