簡體   English   中英

在不同環境中運行C程序時出現分段錯誤

[英]Segmentation fault when running C program in different environments

我最近開始用C編程,但是在名為run.codes的站點上提交作業時遇到了麻煩。 我的程序可以在計算機和多個聯機編譯器中正常運行,但是run.codes在每個測試用例上都給我一個“分段錯誤”錯誤 我懷疑它與站點使用的C版本有關,因此我試圖使程序與舊標准(C99)兼容,但仍然無法正常工作。

在我測試過的所有在線編譯器中,只有一個給了我與run.codes相同的錯誤。 在其他所有程序中,我的程序均按預期工作。 由於“分段錯誤”錯誤通常表明發生了不允許的訪問內存的嘗試,因此我嘗試使用Valgrind跟蹤可能的錯誤。 我的程序碰巧有幾個,即使它可以在大多數環境中編譯並正常工作。

下面是我的程序。 我對過分評論和使用葡萄牙語表示歉意。 我的教授要求做到這一點。

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


/*
 * Definicao de constantes. 
 */
//Tamanhos limites (por seguranca, sao usados valores maiores do que o necessario!)
#define MAX_CHARS 600
#define MAX_SENTENCES 100

//Tipos de perguntas
enum {
    POSITIVOS = 101, NEGATIVOS = 102, PALAVRA = 103
};

//Listas de palavras
const char *POSITIVE_WORDS[] = {"bom", "maravilhoso", "otimo", "sensacional", "excelente", "adorei", "gostei", "amei", "eficiente", "boa", "maravilhosa", "otima"};
const char *NEGATIVE_WORDS[] = {"detestei", "odiei", "ruim", "pessimo", "terrivel", "raiva", "odio", "pessima", "lento", "lenta", "fragil", "desisti"};
const char *INVERSION_WORDS[] = {"nao", "jamais", "nunca"};


/*
 * Implementacao de funcoes nao existentes no C99.
 */
//strdup (copia uma string)
char *my_strdup(char *src)
{
    size_t len = strlen(src) + 1; //o +1 serve para que o simbolo \0 seja copiado tambem
    char *s = malloc(len);

    if (s == NULL)
        return NULL;

    return (char *) memcpy(s, src, len); //memcpy retornara um ponteiro para s
}
//strsep (separa uma string usando um delimitador)
char *my_strsep(char** stringp, const char* delim)
{
  char *p, *start = *stringp;
  p = (start != NULL) ? strpbrk(start, delim) : NULL;

  if (p == NULL) {
    *stringp = NULL;
  }
  else {
    *p = '\0';
    *stringp = p + 1;
  }

  return start;
}


/*
 * Estrutura de um produto.
 */
typedef struct
{
    char *name; //nome do produto
    char sentences[MAX_SENTENCES][MAX_CHARS]; //sentencas que se referem ao produto
    int sentences_count; //numero de sentencas que se referem ao produto    
} Product;


/*
 * Associa uma sentenca a um produto.
 *
 * Product *p: produto em questao.
 * char *sentence: string contendo a sentenca.
 */
void addSentenceToProduct(Product *p, char *sentence)
{
    if(p->sentences_count < MAX_SENTENCES) {
        strcpy(p->sentences[p->sentences_count], sentence);
        p->sentences_count++;
    }
}


/*
 * Remove o ultimo caractere de uma string caso ele seja igual a c.
 *
 * char *str: a string que deseja-se alterar.
 * char c: o caractere que deve estar na ultima posicao da string.
 */
void removeLastChar(char *str, char c)
{
    int len = strlen(str);
    if(len > 0 && str[len-1] == c)
        str[len-1] = '\0';
}


/*
 * Le sentencas com multiplas palavras e as armazena na matriz dada.
 *
 * int count: quantidade de sentencas.
 * char sentencas[][]: matriz onde as sentencas serao armazenadas.
 */
void readSentences(int count, char sentences[][MAX_CHARS])
{
    for(int i = 0; i < count; i++) {
        fgets(sentences[i], MAX_CHARS, stdin);

        //ignora a entrada caso ela seja apenas um line breaker
        if(sentences[i][0] == '\n') {
            i--;
            continue;
        }

        //remove o line breaker do final da string, caso haja um
        removeLastChar(sentences[i], '\n');
    }
}


/*
 * Le sentencas na qual um produto é especificado no inicio. A funcao strsep() é usada para separar o produto do restante da sentenca.
 *
 * int count: quantidade de sentencas.
 * Product products[]: lista com os produtos
 * 
 */
int readSentencesWithProducts(int count, Product products[])
{
    char temp[MAX_SENTENCES][MAX_CHARS];
    readSentences(count, temp); //lendo a frase completa

    int productsCount = 0;
    for(int i = 0; i < count; i++) {
        char *temp2 = my_strdup(temp[i]);

        char *productName = my_strsep(&temp2, ";");
        removeLastChar(productName, ' '); //remove o espaco no final do nome do produto

        //Verifica se o produto ja esta na lista
        int index = -1; 
        for(int j = 0; j < productsCount; j++) {
            if(strcmp(productName, products[j].name) == 0) {
                index = j;
            }
        }

        //Novo produto, caso nao haja nenhum com o nome especificado
        if(index < 0) {
            index = productsCount;
            productsCount++;

            products[index].name = productName;
            products[index].sentences_count = 0;
        }

        //Descobre a sentenca e a adiciona ao produto associado
        char *sentence = my_strsep(&temp2, ";"); //separando sentencas
        addSentenceToProduct(&products[index], sentence);

        free(temp2); //liberando a memoria alocada com o strdup()
    }

    return productsCount;
}


/*
 * Retorna o index (referente a array de produtos) do produto ao qual a pergunta se refere. Retorna -1 caso o produto nao seja encontrado.
 *
 * char *question: referencia para a string que contem a pergunta.
 * Products products[]: array que contem os produtos.
 * int count: numero de produtos
 */
int findProductIndex(char *question, Product products[], int count)
{
    for(int i = 0; i < count; i++) {
        if(strstr(question, products[i].name) != NULL)
            return i;
    }

    return -1;
}


/*
 * Busca pela intencao da pergunta (o que ela quer saber).
 *
 * char *question: referencia para a string contendo a pergunta.
 */
int getQuestionIntent(char *question) 
{
    if(strstr(question, "palavra") != NULL)
        return PALAVRA;
    if(strstr(question, "negativos") != NULL)
        return NEGATIVOS;

    return POSITIVOS;
}


/*
 * Retorna a quantidade de vezes que uma dada palavra aparece em uma sentenca.
 *
 * char *word: palavra cujo uso sera contado.
 * char *sentence: sentenca que sera analisada.
 */
int countWordUsage(const char *word, char *sentence)
{
    int counter = 0;

    char *temp = my_strdup(sentence);
    char *resultWord = my_strsep(&temp, " ");

    while(resultWord != NULL) {
        if(strcmp(word, resultWord) == 0) 
            counter++;

        resultWord = my_strsep(&temp, " ");
    }       

    free(temp);
    return counter;
}


/*
 * Retorna 1 caso a palavra dada seja um inversor e 0 caso nao.
 *
 * char *word: palavra que sera checada.
 */
int isInversor(char *word)
{
    for(int i = 0; i < 3; i++) {
        if(strcmp(word, INVERSION_WORDS[i]) == 0)
            return 1;
    }
    return 0;
}


/*
 * Retorna 1 caso a palavra dada pertenca a classe das "positivas".
 *
 * char *word: palavra que sera analisada.
 */
int isPositive(char *word) {
    for(int i = 0; i < 12; i++) {
        if(strcmp(word, POSITIVE_WORDS[i]) == 0)
            return 1;
    }
    return 0;
}


/*
 * Retorna 1 caso a palavra dada pertenca a classe das "negativas".
 *
 * char *word: palavra que sera analisada.
 */
int isNegative(char *word) {
    for(int i = 0; i < 12; i++) {
        if(strcmp(word, NEGATIVE_WORDS[i]) == 0)
            return 1;
    }
    return 0;
}


/*
 * Retorna a quantidade de vezes que palavras de uma dada lista, levando-se em consideracao inversores, aparecem nas sentencas de um produto.
 *
 * Product p: produto que sera analisado.
 * int dividend: o dividendo da razao (POSITIVO ou NEGATIVO)
 */
double countPosNegRatio(Product p, const int dividend)
{
    int pos = 0, neg = 0, lastInversor = 4;
    for(int i = 0; i < p.sentences_count; i++) 
    {
        char *sentence = my_strdup(p.sentences[i]);
        char *word = my_strsep(&sentence, " ");

        int countedPos = 0, countedNeg = 0; //verifica se ja foi contada uma opiniao positiva ou negativa na sentenca em questao    
        while(word != NULL) 
        {
            if(isInversor(word)) {
                lastInversor = 1;
            }
            else {
                if(lastInversor > 3) {
                    //Positiva
                    if(isPositive(word) && !countedPos) {
                        pos++;
                        countedPos = 1;
                    }
                    //Negativa
                    else if(isNegative(word) && !countedNeg) {
                        neg++;
                        countedNeg = 1;
                    }
                }
                else {
                    //Negativa (inversao)
                    if(isPositive(word) && !countedNeg) {
                        neg++;
                        countedNeg = 1;
                    }
                    //Positiva(inversao)
                    else if(isNegative(word) && !countedPos) {
                        pos++;
                        countedPos = 1;
                    }
                }
                lastInversor++;
            }
            word = my_strsep(&sentence, " ");
        }
        free(sentence);
    }

    int total = pos + neg;
    return (dividend == POSITIVOS) ? (double) pos/total : (double) neg/total;
}


/*
 * Processa e responde a uma pergunta.
 *
 * char *question: pergunta que deve ser respondida.
 * Products products[]: lista de produtos
 * int count: quantidade total de produtos
 */
void processQuestion(char *question, Product products[], int count)
{
    int pIndex = findProductIndex(question, products, count);

    if(pIndex >= 0) 
    {
        Product p = products[pIndex];
        int intent = getQuestionIntent(question);

        switch(intent)
        {
            case POSITIVOS:
            case NEGATIVOS:
            {
                printf("%.1lf%%\n", 100*countPosNegRatio(p, intent));
                break;
            }
            case PALAVRA:
            {

                //descobre a palavra que se deseja buscar
                char *temp = my_strdup(question);
                my_strsep(&temp, " "); //descarta a primeira palavra
                char *word = my_strsep(&temp, " "); //armazena a segunda palavra

                //checa quantas vezes a palavra apareceu
                int counter = 0;
                for(int i = 0; i < p.sentences_count; i++) {
                    counter += countWordUsage(word, p.sentences[i]);
                }

                printf("%d\n", counter);
                break;
            }
            default:
                printf("Nao foi possivel detectar a intencao da pergunta!\n");
        }
    }
    else {
        printf("Produto nao encontrado!\n");
    }
}


/*
 * MAIN
 */
int main()
{
    int sentences_count, questions_count; 
    scanf("%d %d", &sentences_count, &questions_count);

    Product products[MAX_SENTENCES]; //armazena os produtos e suas respectivas sentencas
    int products_count = readSentencesWithProducts(sentences_count, products);

    char questions[MAX_SENTENCES][MAX_CHARS]; //armazena as perguntas
    readSentences(questions_count, questions);

    for(int i = 0; i < questions_count; i++) {
        processQuestion(questions[i], products, products_count);
    }


    /*
    //DEBUG
    for(int i = 0; i < productsCount; i++) {
        Product p = products[i];
        printf("-> %s:\n", p.name);
        for(int j = 0; j < p.sentences_count; j++) {
            printf("    %d - %s\n", j, p.sentences[j]); 
        }
    }
    */

    return 0;
}

該程序應該閱讀有關某些產品的注釋,然后回答一些問題,例如 作為輸入提供的前兩個整數分別指定評論的數量和問題的數量。 輸入的其余部分包括所有注釋以及所有問題。 . 每個注釋的格式都這樣: 輸出包括每個問題的答案。

是當我使用以下輸入時Valgrind生成的日志:

3 1
Televisor LG ; adorei o televisor lg ele e muito funcional Ø
Laptop HP ; nao gostei desse notebook pois ele esquenta muito Ø
Celular Motorola ; o formato do celular e muito bom bom mesmo bom demais Ø
palavra bom em Celular Motorola

預期輸出(3)由程序給出。 我正在使用以下命令來編譯我的代碼:

gcc trab.c -o trab -Wall -ggdb3

並使用以下參數初始化Valgrind:

valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --log-file=valgrind-out.txt ./trab

最后,在給了您所有這些信息之后,我的問題是: 為什么我的程序僅在某些環境下可以工作,我應該怎么做才能解決這個問題? 我討厭這篇文章的篇幅如此之大,但是由於問題很奇怪,我覺得我應該提供所有可能的信息。 謝謝你的時間!

注意:我的操作系統是Ubuntu 19.04,並且這些程序( 此處此處此處此處 )是一些在線編譯器,這些程序可以在其中成功運行程序。 是我遇到分段錯誤錯誤的在線編譯器。

在朋友的幫助下,我設法解決了我的問題! 我將解決方案發布在這里,以防將來有人遇到類似問題。

Valgrind中顯示的大多數錯誤是由堆棧溢出引起的 我的程序只是使用過多的內存。 為了解決這個問題,我將products數組的大小更改為合理的大小(我使用的數量遠遠超過了我實際需要的數量!)。 往下看:

#define MAX_SENTENCES 32 //It was 100 before!

請注意,即使Valgrind檢測到的錯誤數量已大大減少,但程序中仍然存在一些內存泄漏!

不幸的是,這還不足以讓run.codes接受我的程序。 某些操作系統使用\\r作為換行符(通常為\\n )。 我正在使用fgets()函數從標准輸入中讀取字符串。 它還會讀取我不感興趣的換行符,因此我檢查了它們,以便將其丟棄。 但是, 我只檢查\\n

void readSentences(int count, char sentences[][MAX_CHARS]) {
        //...
        fgets(sentences[i], MAX_CHARS, stdin);
        if(sentences[i][0] == '\n') {
            i--;
            continue;
        }
        //...
}

我真的還應該檢查\\r

if(sentences[i][0] == '\n' || sentences[i][0] == '\r') //{ ... }

這是在某些環境中導致“細分錯誤”錯誤的原因。 解決之后,我的程序在它們上運行良好!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM