简体   繁体   English

在不同环境中运行C程序时出现分段错误

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

I've recently started programming in C and I'm having trouble submitting a homework on a site called run.codes . 我最近开始用C编程,但是在名为run.codes的站点上提交作业时遇到了麻烦。 My program works fine in my computer and in several online compilers, but run.codes is giving me a "Segmentation fault" error on every test case . 我的程序可以在计算机和多个联机编译器中正常运行,但是run.codes在每个测试用例上都给我一个“分段错误”错误 I suspected it had something to do with the C version that the site is using, so I tried to make my program compatible with older standards (C99), but it still not working. 我怀疑它与站点使用的C版本有关,因此我试图使程序与旧标准(C99)兼容,但仍然无法正常工作。

Of all the online compilers I've tested, only this one gave me the same error as run.codes. 在我测试过的所有在线编译器中,只有一个给了我与run.codes相同的错误。 In all the others, my program worked as expected. 在其他所有程序中,我的程序均按预期工作。 Since the "segmentation fault" error usually indicates that an unallowed attempt to access memory has ocurred, I've tried using Valgrind to track possible errors. 由于“分段错误”错误通常表明发生了不允许的访问内存的尝试,因此我尝试使用Valgrind跟踪可能的错误。 My program happens to have several, even though it compiles and works fine in most environments. 我的程序碰巧有几个,即使它可以在大多数环境中编译并正常工作。

Below is my program. 下面是我的程序。 I apologize the over-commenting and for the use of portuguese. 我对过分评论和使用葡萄牙语表示歉意。 My professor demands it to be that way. 我的教授要求做到这一点。

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

The program is supposed to read comments about certain products and then answer some questions, like "how many times was the word X used?" 该程序应该阅读有关某些产品的注释,然后回答一些问题,例如“ X词使用了多少次?” or "what's the percentage of positive comments about product Y?" “对产品Y的正面评论所占的百分比是多少?” . The two first integers given as input specify, respectively, the number of comments and the number of questions. 作为输入提供的前两个整数分别指定评论的数量和问题的数量。 The rest of the input consists on all the comments followed by all the questions. 输入的其余部分包括所有注释以及所有问题。 Each comment is formatted this way: "name of the product ; comment" . 每个注释的格式都这样: “产品名称;注释” The output consists of an answer for each of the questions. 输出包括每个问题的答案。

Here is the log generated by Valgrind when I use the input below: 是当我使用以下输入时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

The expected output (3), is given by the program. 预期输出(3)由程序给出。 I'm using the following command to compile my code: 我正在使用以下命令来编译我的代码:

gcc trab.c -o trab -Wall -ggdb3

And the following parameters to initialize Valgrind: 并使用以下参数初始化Valgrind:

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

Finally, having given you all this information, my question is: why does my program only work on certain environments and what can I do to solve this? 最后,在给了您所有这些信息之后,我的问题是: 为什么我的程序仅在某些环境下可以工作,我应该怎么做才能解决这个问题? I hate that the post ended up this big, but since the problem is weird, I've felt that I should give all the information I could. 我讨厌这篇文章的篇幅如此之大,但是由于问题很奇怪,我觉得我应该提供所有可能的信息。 Thanks for your time! 谢谢你的时间!

Note: my operational system is Ubuntu 19.04 and these ( here , here , here and here ) are some of the online compilers in which my program has run successfully. 注意:我的操作系统是Ubuntu 19.04,并且这些程序( 此处此处此处此处 )是一些在线编译器,这些程序可以在其中成功运行程序。 And this is the online compiler in which I get a segmentation fault error. 是我遇到分段错误错误的在线编译器。

With the help of a friend, I've managed to solve my problem! 在朋友的帮助下,我设法解决了我的问题! I'm posting the solution here in case someone runs into a similar problem in the future. 我将解决方案发布在这里,以防将来有人遇到类似问题。

Most of the errors shown in Valgrind were caused by a stack overflow . Valgrind中显示的大多数错误是由堆栈溢出引起的 My program was simply using too much memory. 我的程序只是使用过多的内存。 To solve that, I've changed the size of the products array to a reasonable one (I was using much more than what I actually needed!). 为了解决这个问题,我将products数组的大小更改为合理的大小(我使用的数量远远超过了我实际需要的数量!)。 Look below: 往下看:

#define MAX_SENTENCES 32 //It was 100 before!

Note that even though the number of errors detected by Valgrind has decreased considerably with this, there are still some memory leaks in my program! 请注意,即使Valgrind检测到的错误数量已大大减少,但程序中仍然存在一些内存泄漏!

This, unfortunately, wasn't enough for run.codes to accept my program. 不幸的是,这还不足以让run.codes接受我的程序。 Some operational systems use \\r as the new line character (usually \\n ). 某些操作系统使用\\r作为换行符(通常为\\n )。 I was using the function fgets() to read strings from the stdin. 我正在使用fgets()函数从标准输入中读取字符串。 It also reads new line characters, in which I'm not interested, so I checked for them so they could be discarded. 它还会读取我不感兴趣的换行符,因此我检查了它们,以便将其丢弃。 However, I was only checking for \\n : 但是, 我只检查\\n

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

When I really should also be checking for \\r : 我真的还应该检查\\r

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

This was the cause of the "Segmentation Fault" error on some environments. 这是在某些环境中导致“细分错误”错误的原因。 After solving that, my program worked fine on them! 解决之后,我的程序在它们上运行良好!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM