我无法修复这个消毒剂错误,它说我使用已经释放的内存

问题描述 投票:0回答:1

我似乎遇到了这个消毒剂错误,但我不知道该怎么做或如何纠正它。我已经尝试了一切,但它似乎仍然存在。根据我的理解,内存在释放后正在被使用。但我似乎找不到它。也许是关于指向此类内存的指针?我不知道。请帮助我。

==51931==ERROR: AddressSanitizer: heap-use-after-free on address 0x000107701e10 at pc 0x000104b76ed8 bp 0x00016b28a230 sp 0x00016b28a228
READ of size 8 at 0x000107701e10 thread T0
    #0 0x104b76ed4 in freeClass hw1.c:40
    #1 0x104b772d8 in removeClassFromArray hw1.c:61
    #2 0x104b7ad70 in updateEquivalenceClasses hw1.c:414
    #3 0x104b7d038 in roundOfGame hw1.c:634
    #4 0x104b7e954 in main hw1.c:774
    #5 0x18a9360dc  (<unknown module>)

0x000107701e10 is located 0 bytes inside of 24-byte region [0x000107701e10,0x000107701e28)
freed by thread T0 here:
    #0 0x105427260 in wrap_free+0x98 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x53260)
    #1 0x104b77124 in freeClass hw1.c:52
    #2 0x104b7ad5c in updateEquivalenceClasses hw1.c:412
    #3 0x104b7d038 in roundOfGame hw1.c:634
    #4 0x104b7e954 in main hw1.c:774
    #5 0x18a9360dc  (<unknown module>)

previously allocated by thread T0 here:
    #0 0x105427124 in wrap_malloc+0x94 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x53124)
    #1 0x104b7b034 in createDictionary hw1.c:438
    #2 0x104b7e6bc in main hw1.c:767
    #3 0x18a9360dc  (<unknown module>)

SUMMARY: AddressSanitizer: heap-use-after-free hw1.c:40 in freeClass

我的代码是关于作弊刽子手游戏的。这是:

#include "hw1.h" 
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>

// Struct που αναπαριστά μια κλάση ισοδυναμίας
typedef struct
{
    char* representative; // Η δυναμικά δεσμευμένη συμβολοσειρά που αναπαριστά τον εκπρόσωπο μιας κλάσης
    int wordCount; // Το πλήθος των λέξεων της κλάσης
    char** words; // Ο πίνακας με δείκτες στις λέξεις της κλάσης
} EquivalenceClass;

// Συνάρτηση για τον έλεγχο των ορισμάτων
errorCodeT checkArguments(int argc, char *argv[], int *wordLength, int *turns)
{
    if (argc != 4) return INVALID_ARGS; // Αν δοθεί λάθος πλήθος ορισμάτων, επέστρεψε INVALID_ARGS

    if (sscanf(argv[1], "%d", wordLength) != 1 || *wordLength <= 0) // Έλεγχος για το αν το μήκος της λέξης είναι μη αρνητικό
    {
        return INVALID_LEN;
    }

    if (sscanf(argv[2], "%d", turns) != 1 || *turns <= 0) // Έλεγχος για το αν το πλήθος των γύρων είναι μη αρνητικό
    {
        return INVALID_TURNS;
    }

    // Αν είναι όλα οκ, επέστρεψε ΟΚ
    return OK;
}

// Συνάρτηση που αποδεσμεύει το περιεχόμενο μιας κλάσης μαζί με αυτή
void freeClass(EquivalenceClass* eqClass) 
{
    if (!eqClass) return; // Αν είναι null η κλάση

    if (eqClass->representative) // Αποδεσμεύω τον εκπρόσωπο
    {
        free(eqClass->representative);
        eqClass->representative = NULL;
    }

    for (int i = 0; i < eqClass->wordCount; i++) 
    {
        free(eqClass->words[i]); // Αποδεσμεύω κάθε λέξη
    }
    free(eqClass->words); // Αποδεσμέυω τον πίνακα δεικτών προς λέξεις
    eqClass->words = NULL;
    free(eqClass); // Αποδεσμεύω την κλάση
    
}

// Συνάρτηση που αποδεσμεύει μια κλάση απο τον πίνακα δεικτών προς κλάσεις
void removeClassFromArray(EquivalenceClass*** ptrToArray, int* classCount, int index) 
{
    if (!ptrToArray || !*ptrToArray || index < 0 || index >= *classCount) return; // Invalid input

    freeClass((*ptrToArray)[index]); // Αποδεσμεύω την κλάση
    (*ptrToArray)[index] = NULL;

    // Μετακινώ τις κλάσεις που έμειναν για να καλύψουν το κενό
    for (int i = index; i < (*classCount) - 1; i++) 
    {
        (*ptrToArray)[i] = (*ptrToArray)[i + 1];
    }

    (*classCount)--; // Μειώνω το πλήθος κλάσεων

    // Αλλάζω το μέγεθος του πίνακα δεικτών
    if (*classCount > 0) 
    {
        // Προσωρινός πίνακας
        EquivalenceClass** tempPtr = realloc(*ptrToArray, (*classCount) * sizeof(EquivalenceClass*));
        if (!tempPtr) // Αν αποτύχει η realloc
        {
            printf("Memory error in resizing array.\n");
            exit(EXIT_FAILURE);
        }
        *ptrToArray = tempPtr;
    } 
    else // Αλλιώς αν δεν έχουν μείνει άλλες κλάσεις στον πίνακα
    {
        free(*ptrToArray);
        *ptrToArray = NULL;
    }
}

// Συνάρτηση που αποδεσμεύει τον πίνακα με τις κλάσεις
void freeClassesArray(EquivalenceClass*** ptrToClassesArray, int classCount) 
{
    for (int i = 0; i < classCount; ++i) 
    {
        free((*ptrToClassesArray)[i]->representative);
        for (int j = 0; j < (*ptrToClassesArray)[i]->wordCount; ++j) 
        {
            free((*ptrToClassesArray)[i]->words[j]);
        }
        free((*ptrToClassesArray)[i]->words);
        free((*ptrToClassesArray)[i]);
    }
    free(*ptrToClassesArray);
}

// Συνάρτηση που δημιουργεί εκπρόσωπο με βάση τη λέξη και το guess
char* generateNewRepresentative(const char* currentRepresentative, const char* word, char guess) 
{
    size_t len = strlen(word);
    char* newRep = (char*)malloc(len + 1); // +1 για το null terminator
    if (!newRep) 
    {
        printf("Memory error.\n");
        exit(EXIT_FAILURE);
    }

    guess = toupper(guess); //οθέλω το guess να είναι κεφαλαία

    for (size_t i = 0; i < len; i++) 
    {
        // Αν το γράμμα ταιριάξει με το guess, το φανερώνω
        if (toupper(word[i]) == guess) 
        {
            newRep[i] = guess;
        } 
        else // Αλλίως άσε τον εκπρόσωπο όπως ήταν πριν σε αυτο το σημείο 
        {
            newRep[i] = currentRepresentative[i];
        }
    }
    newRep[len] = '\0'; // Null στο τέλος

    return newRep;
}

// Συνάρτηση που διαλέγει την καλύτερη κλάση με σκοπό να δυσκολέψει τον παίκτη
EquivalenceClass* chooseBestClass(EquivalenceClass** ptrToClassesArray, int classCount) 
{
    EquivalenceClass* bestClassPtr = NULL; // Δείκτης στην καλύτερη κλάση
    int maxWordCount = -1;
    int maxHiddenLetters = -1;

    if (classCount == 0) return NULL;

    for (int i = 0; i < classCount; i++) // Για κάθε κλάση στον πίνακα
    {   
        EquivalenceClass* currentClassPtr = ptrToClassesArray[i]; // Δείκτης στην τρέχουσα κλάση
        int hiddenLetters = 0;
        // Μέτρηση κρυφών γραμμάτων
        for (size_t j = 0; j < strlen(currentClassPtr->representative); j++) 
        {
            if (currentClassPtr->representative[j] == '_') hiddenLetters++; // Αν υπάρχει "_" τότε αύξησε τα "κρυμμένα γράμματα"
        }

        // Αναζήτηση για την κλάση με το μέγιστο πλήθος λέξεων ή κρυφών γραμμάτων
        if (currentClassPtr->wordCount > maxWordCount || 
           (currentClassPtr->wordCount == maxWordCount && hiddenLetters > maxHiddenLetters)) 
        {
            maxWordCount = currentClassPtr->wordCount;
            maxHiddenLetters = hiddenLetters;
            bestClassPtr = currentClassPtr;
        }
    }
    return bestClassPtr; // Επιστροφή του δείκτη στην καλύτερη κλάση
}

// Συνάρτηση που φτίαχνει ένα μοτίβο για τον εκπρόσωπο με βάση μια λέξη και το γράμμα που μαντεύτηκε
char* generateRepresentativePattern(const char* word, char guess) 
{
    size_t len = strlen(word);
    char* pattern = (char*)malloc(len + 1); // +1 για το null
    for (size_t i = 0; i < len; i++) 
    {
        pattern[i] = (toupper(word[i]) == toupper(guess)) ? toupper(guess) : '_';
    }
    pattern[len] = '\0';
    return pattern;
}

// Συνάρτηση που ελέγχει αν μια συμβολοσειρά περιέχει έναν συγκεκριμένο χαρακτήρα
bool wordContainsLetter(const char* word, char guess) 
{
    // Μετατροπή του χαρακτήρα σε κεφαλαίο
    guess = toupper((unsigned char)guess);

    while (*word != '\0') // Επανάληψη μέχρι το τέλος της συμβολοσειράς
    {
        // Ελέγχω αν ο κεφαλαίος χαρακτήρας του word είναι ίσος με το guess
        if (toupper((unsigned char)*word) == guess) 
        {
            return true; // Βρέθηκε το γράμμα
        }
        word++; // Μετάβαση στον επόμενο χαρακτήρα
    }
    return false; // Δεν βρέθηκε το γράμμα
}

// Συνάρτηση που συγκρίνει τους εκπροσώπους 2 κλάσεων για την qsort
int compareClasses(const void *a, const void *b) 
{
    // Αντικαταστήστε τον τύπο των δεικτών σε δείκτες προς δείκτες της EquivalenceClass
    EquivalenceClass *classA = *(EquivalenceClass **)a;
    EquivalenceClass *classB = *(EquivalenceClass **)b;
    // Συγκρίνετε τους εκπροσώπους των δύο κλάσεων
    return strcmp(classA->representative, classB->representative);
}

// Συνάρτηση που αναφαθμίζει τον εκπρόσωπο ανάλογα με το γράμμα που μαντεύει ο παίκτης
void updateRepresentative(EquivalenceClass* eqClass, const char* word, char guess) 
{
    guess = toupper(guess); // Θέλω το guess να είναι κεφαλαία

    // Ελέγχω κάθε γράμμα στη λέξη
    for (size_t i = 0; word[i] != '\0'; i++) 
    {
        // Αν το γράμμα ταιριάξει με το guess και δεν έχει ήδη αποκαλυφθεί
        if (toupper(word[i]) == guess && eqClass->representative[i] == '_') 
        {
            eqClass->representative[i] = guess; // Φανερώνω το γράμμα
        }
    }
}

// Συνάρτηση που προσθέτει μια λέξη μέσα σε μια κλάση
void addWordToClass(EquivalenceClass* eqClass, char* word) 
{
    // Μεγαλώνω τον χώρο για να χωράει άλλη μια λέξη
    eqClass->words = (char**)realloc(eqClass->words, (eqClass->wordCount + 1) * sizeof(char*));
    if (!eqClass->words)
    {
        printf("Memory error.\n");
        exit(EXIT_FAILURE);
    }
    // Προσθέτω τη λέξη στην κλάση
    eqClass->words[eqClass->wordCount] = strdup(word); // Αντιγράφω τη λέξη
    eqClass->wordCount++; // Αυξάνω το πλήθος λέξεεων
}

// Συνάρτηση που αφαιρεί μια λέξη απο μια κλάση
void removeWordFromClass(EquivalenceClass* eqClass, int wordIndex) 
{
    if (wordIndex < 0 || wordIndex >= eqClass->wordCount) 
    {
        printf("Invalid word index.\n");
        return;
    }

    // Απελευθερώνω τη λέξη
    free(eqClass->words[wordIndex]);

    // Μεταφέρω τις λέξεις που έμειναν για να καλύψουν το κενό
    for (int i = wordIndex; i < eqClass->wordCount - 1; i++) 
    {
        eqClass->words[i] = eqClass->words[i + 1];
    }
    // Μειώνω το πλήθος λέξεων
    eqClass->wordCount--;

    // Ελέγχω αν πρέπει να αλλάξω το μέγεθος του πίνακα ή να τον θέσω NULL αν είναι 
    if (eqClass->wordCount > 0) 
    {
        // Αλλάζω το μέγεθος του πίνακα
        char** tempWords = realloc(eqClass->words, eqClass->wordCount * sizeof(char*));
        if (!tempWords) {
            printf("Memory error.\n");
            exit(EXIT_FAILURE);
        }
        eqClass->words = tempWords;
    } 
    else 
    {
        // Αν είναι άδειος ο πίνακας, τον αποδεσμεύω
        free(eqClass->words);
        eqClass->words = NULL;
    }
}

// Συνάρτηση που αφαιρεί μια κλάση απο τον πίνακα δεικτών
void removeClass(EquivalenceClass*** ptrArray, int* classCount, int index) 
{
    free((*ptrArray)[index]->representative); // Αποδεσμεύω τον εκπρόσωπο της
    for (int i = 0; i < (*ptrArray)[index]->wordCount; ++i) 
    {
        free((*ptrArray)[index]->words[i]); // Αποδεσμεύω κάθε λέξη
    }
    free((*ptrArray)[index]->words); // Αποδεσμεύω τον πίνακα λέξεων
    free((*ptrArray)[index]); // Αποδεσμεύω την κλάση

    // Μετακινώ τις κλάσεις που έμειναν για να καλύψουν το κενό
    for (int i = index; i < *classCount - 1; ++i) 
    {
        (*ptrArray)[i] = (*ptrArray)[i + 1];
    }
    (*classCount)--; // Μειώνω το πλήθος κλάσεων

    // Αλλάζω το μέγεθος του πίνακα δεικτών
    *ptrArray = (EquivalenceClass**)realloc(*ptrArray, (*classCount) * sizeof(EquivalenceClass*));
    if (*classCount > 0 && !*ptrArray) 
    {
        printf("Memory error.\n");
        exit(EXIT_FAILURE);
    }
}

// Συνάρτηση που ψάχνει αν υπάρχει κλάση με συγκεκριμένο εκπρόσωπο ή δημιουργεί μια αν δεν βρεί
EquivalenceClass* findOrCreateClassWithRepresentative(EquivalenceClass*** ptrArray, int* classCount, const char* representative) 
{
    // Βρες μια κλάση που έχει τον ίδιο εκπρόσωπο
    for (int i = 0; i < *classCount; i++) 
    {

        if (strcmp((*ptrArray)[i]->representative, representative) == 0) {
            // Βρήκε κλάση
            return (*ptrArray)[i];
        }
    }

    // Αν δεν βρεις κλάση, φτίαξε μια
    EquivalenceClass* newClass = (EquivalenceClass*)malloc(sizeof(EquivalenceClass));
    newClass->representative = strdup(representative);
    newClass->wordCount = 0;
    newClass->words = (char**)malloc(sizeof(char*)); // Αρχικά θα έχει χώρο για μια λέξη

    // Προσθέτω την κλάση στον πίνακα
    *ptrArray = (EquivalenceClass**)realloc(*ptrArray, (*classCount + 1) * sizeof(EquivalenceClass*));
    (*ptrArray)[*classCount] = newClass;
    (*classCount)++;

    return newClass;
}

// Συνάρτηση που βάζει κλάσεις στον πίνακα δεικτών προς κλάσεις ισοδυναμίας
void mergeClasses(EquivalenceClass*** mainArray, int* mainCount, EquivalenceClass** tempClasses, int tempCount) 
{
    if (tempCount == 0) 
    {
        // Δεν υπάρχουν κλάσεις που να θέλουν συγχώνευση
        return;
    }

    // Αλλάζω τον χώρο για να χωρέσουν οι κλάσεις
    *mainArray = (EquivalenceClass**)realloc(*mainArray, (*mainCount + tempCount) * sizeof(EquivalenceClass*));
    if (!*mainArray) 
    {
        printf("Memory error.\n");
        exit(EXIT_FAILURE);
    }

    // Βάζω κάθε κλάσει στο τέλος του πίνακα δεικτών
    for (int i = 0; i < tempCount; ++i) 
    {
        (*mainArray)[*mainCount + i] = tempClasses[i];
    }

    // Ανανεώνω το πλήθος κλάσεων
    *mainCount += tempCount;
}

// Συνάρτηση που ανανεώνει τον εκπρόσωπο βάση το γράμμα που μαντεύτηκε
void updateEquivalenceClasses(EquivalenceClass*** ptrArray, int* classCount, char guess) 
{
    guess = toupper(guess); // Θέλω να ναι κεφαλαίο το guess

    // Αν υπάρχει μόνο μία κλάση με μία λέξη, απλά άλλαξε τον εκπρόσωπο της
    if (*classCount == 1 && (*ptrArray)[0]->wordCount == 1) 
    {
        updateRepresentative((*ptrArray)[0], (*ptrArray)[0]->words[0], guess);
        return;
    }

    // Για τις νέες αν υπάρξουν κλάσεις
    EquivalenceClass** tempClasses = NULL;
    int tempClassCount = 0;

    // Ελέγχω κάθε κλάση
    for (int i = 0; i < *classCount; ++i) 
    {
        EquivalenceClass* currentClass = (*ptrArray)[i];

        // Ελέγχω κάθε λέξη στην κλάση
        for (int j = 0; j < currentClass->wordCount; ) 
        {
            char* word = currentClass->words[j];
            if (wordContainsLetter(word, guess)) 
            {
                // Φτιάξε εναν προσωρινό νέο εκπρόσωπο με βάση τη λέξη και το γράμμα
                char* newRep = generateNewRepresentative(currentClass->representative, word, guess);

                // Προσπάθησε να βρείς ή να δημιουργήσεις μια κλάση με αυτον τον εκρπόσωπο
                EquivalenceClass* matchingClass = findOrCreateClassWithRepresentative(&tempClasses, &tempClassCount, newRep);

                // Μετέφερε τη λέξη σε αυτήν την κλάση που βρήκες/έκανες
                addWordToClass(matchingClass, word);

                // Αφαίρεσε τη λέξη απο την κλάση που ήταν πρίν
                removeWordFromClass(currentClass, j);

                // Απελευθέρωσε τον προσωρινό εκπρόσωπο
                free(newRep);
            } 
            else 
            {
                // Αυξήσε το j μόνο αν δεν αφαιρέσες λέξη
                ++j;
            }
        }

        // Αν η κλάση είναι άδεια, αποδέσμευσε την
        if (currentClass->wordCount == 0) 
        {
            freeClass(currentClass);
            currentClass = NULL;
            removeClassFromArray(ptrArray, classCount, i);
            --i;
            continue; // Πήγαινε στην επόμενη κλάση
        }
    }

    // Βάζω τις κλάσεις στον πίνακα δεικτών προς κλάσης ισοδυναμίας
    mergeClasses(ptrArray, classCount, tempClasses, tempClassCount);
    free(tempClasses);
}

// Συνάρτηση δημιουργίας λεξικού
EquivalenceClass** createDictionary(const char *fileName, int desiredWordLength, int *totalClasses) 
{
    char *word;
    // Δέσμευση μνήμης στον πίνακα δεικτών
    EquivalenceClass** ptrToClasses = malloc(sizeof(EquivalenceClass*)); 
    if (!ptrToClasses)  // Αν αποτύχει
    {
        *totalClasses = 0;
        return NULL;
    }

    // Δέσμευση μνήμης για το δείκτη της 1 αυτής κλάσης
    *ptrToClasses = malloc(sizeof(EquivalenceClass));
    if (!*ptrToClasses) // Αν αποτύχει
    {
        free(ptrToClasses);
        *totalClasses = 0;
        printf("Memory error.\n");
        exit(EXIT_FAILURE);
    }

    EquivalenceClass* eqClass = *ptrToClasses;
    eqClass->wordCount = 0; // Αρχικά έχω 0 λέξεις
    eqClass->words = NULL; // Δεν έχω πίνακα λέξεων ακόμα
    // Δημιουργώ τον εκπρόσωπο και τον γεμίζω με "_" ανάλογα με το desiredLength
    eqClass->representative = malloc((desiredWordLength + 1) * sizeof(char));
    if(!eqClass->representative)
    {
        free(*ptrToClasses);
        free(ptrToClasses);
        printf("Memory error.\n");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < desiredWordLength; i++) 
    {
        eqClass->representative[i] = '_';
    }
    eqClass->representative[desiredWordLength] = '\0'; // Null στο τέλος

    int wordArrayCapacity = 0; // Για να ελέγχω την αλλαγή στο μέγεθος του πίνακα των λέξεων

    eqClass->words = NULL; // Για να βεβαιωθώ πως η realloc μπορεί να χρησιμοποιηθεί σαν malloc την πρώτη φορά που τη χρησιμοποιώ

    while ((word = getWord((char*)fileName)) != NULL)
    {
        // Μετατρέπω τη λέξη σε κεφαλαία
        for (int i = 0; word[i]; i++) 
        {
            word[i] = toupper((unsigned char)word[i]);
        }

        if (strlen(word) == desiredWordLength) // Ελέγχω αν το μήκος της ταιριάζει στο desiredLength
        {
            // Μεγαλώνω τον πίνακα των λέξεων αν χρειαστεί
            if (eqClass->wordCount == wordArrayCapacity)
            {
                wordArrayCapacity = wordArrayCapacity > 0 ? wordArrayCapacity * 2 : 10;
                char** tempWords = realloc(eqClass->words, wordArrayCapacity * sizeof(char*));
                if (!tempWords) // Αν αποτύχει
                {
                    for (int i = 0; i < eqClass->wordCount; i++)
                    {
                        free(eqClass->words[i]);
                    }
                    free(eqClass->words);
                    free(eqClass->representative);
                    free(*ptrToClasses);
                    free(ptrToClasses);
                    printf("Memory error.\n");
                    exit(EXIT_FAILURE);
                }
                eqClass->words = tempWords;
            }

            // Βάζω τη λέξη στον πίνακα λέξεων
            eqClass->words[eqClass->wordCount] = strdup(word);
            if(!eqClass->words[eqClass->wordCount])
            {
                for (int i = 0; i < eqClass->wordCount; ++i)
                    {
                        free(eqClass->words[i]);
                    }
                    free(eqClass->words);
                    free(eqClass->representative);
                    free(*ptrToClasses);
                    free(ptrToClasses);
                    printf("Memory error.\n");
                    exit(EXIT_FAILURE);
            }
            eqClass->wordCount++;
        }
        free(word);
    }

    *totalClasses = 1;
    return ptrToClasses;
}

// Συνάρτηση που εμφανίζει τα ήδη χρησιμοποιημένα, αλλά και τα μη χρησιμοποιημένα γράμματα
void printLetters(const bool lettersUsed[26]) 
{
    printf("Used letters: "); // Εμφάνιση των χρησιμοποιημένων γραμμάτων
    for (char c = 'A'; c <= 'Z'; ++c) // Για κάθε χαρακτήρα στο αλφάβητο
    {
        if (lettersUsed[c - 'A']) // Αν έχει χρησιμοποιηθεί το γράμμα, εκτύπωσε το
        {
            printf(" %c", c);
        }
    }
    printf("\n");

    printf("Unused letters: "); // Εκτύπωση των μη χρησιμοποιημένων γραμμάτων
    for (char c = 'A'; c <= 'Z'; ++c) 
    {
        if (!lettersUsed[c - 'A'])  // Αν δεν έχει χρησιμοποιηθεί το γράμμα, εκτύπωσε το
        {
            printf(" %c", c);
        }
    }
}

// Συνάρτηση για την καταγραφή νέου γράμματος που μαντέψτηκε
bool markLetter(bool lettersUsed[26], char guess) 
{
    guess = toupper(guess);
    if (guess < 'A' || guess > 'Z' || lettersUsed[guess - 'A']) {
        return false;
    }
    lettersUsed[guess - 'A'] = true;
    return true;
}

// Συνάρτηση που ρωτάει τον παίκτη αν θέλει να ξανά παίξει
bool askToPlayAgain() 
{
    char answer;
    do 
    {
        printf("\nPlay again? (y/n)\n");
        scanf(" %c", &answer);
    } while (answer != 'y' && answer != 'n');
    return answer == 'y';
}

// Για την qsort για τις λέξεις. Για να τις τυπώσω λεξικογραφικά
int compareWords(const void *a, const void *b) 
{
    const char *word1 = *(const char **)a;
    const char *word2 = *(const char **)b;

    return strcmp(word1, word2);
}


// Συνάρτηση που αναπαριστά ενα γύρο παιχνιδιού
void roundOfGame(EquivalenceClass*** ptrToClassesArray, int totalWords, int turns, int *classCount) 
{
    bool lettersUsed[26] = {false};
    char guess;
    int currentRound = 1;
    int playerScore = 0;
    int computerScore = 0;
    EquivalenceClass* currentClass = NULL; 
    EquivalenceClass* chosenClass = NULL;
    bool gameWon = false;

    while(currentRound <= turns && !gameWon) // Όσο δεν έχουν τελείωσει οι προσπάθειες του παίκτη
    {
        printf("\n== ROUND %d/%d ==\n\n", currentRound, turns);
        printLetters(lettersUsed); // Συνάρτηση που εκτυπώνει τα χρησιμοποιημένα και μη γράμματα

        currentClass = chooseBestClass(*ptrToClassesArray, *classCount); // όπου currentClass pointer στην επιλεγμένη κλάση
        if (!currentClass || !(currentClass->representative)) 
        {
            printf("Memory error.\n");
            exit(EXIT_FAILURE);
        }
        
        printf("\nWord: ");
        for (size_t i = 0; i < strlen(currentClass->representative); i++) 
        {
            if (currentClass->representative[i] != ' ') 
            {
                printf(" %c", currentClass->representative[i]);
            }
        }
        printf("\n");

        bool validGuess = false;
        while (!validGuess) 
        {
            printf("\nGuess a letter: ");
            scanf(" %c", &guess);
            guess = toupper(guess); // Κάντο κεφαλαία
        
            if (guess < 'A' || guess > 'Z') 
            { // Αν δεν είναι γράμμα
                printf("%c is not a letter.\n", guess);
            } 
            else if (lettersUsed[guess - 'A']) 
            { // Αν έχει ξαναχρησιμοποιηθεί το γράμμα
                printf("You have already guessed %c.\n", guess);
            }
            else // Αν η guess είναι γράμμα
            {
                validGuess = true;
                markLetter(lettersUsed, guess); // Σημείωσε το γράμμα ως χρησιμοποιημένο πλέον
                updateEquivalenceClasses(ptrToClassesArray, classCount, guess); // Ανανέωσε τις κλάσεις
                chosenClass = chooseBestClass(*ptrToClassesArray, *classCount);
                if(!chosenClass)
                {
                    printf("Memory error.\n");
                    exit(EXIT_FAILURE);
                }

                currentClass = chosenClass;

                #ifdef DEBUG

                    // Ταξινομώ τις κλάσεις λεξικογραφικά
                    qsort(*ptrToClassesArray, *classCount, sizeof(EquivalenceClass*), compareClasses);

                    printf("\n== CLASSES ==\n");
                    printf("%d\n\n", *classCount);

                    for (int i = 0; i < *classCount; i++)
                    {
                        // Όπου classPtr είναι ο δείκτης που δείχνει στον πίνακα δεικτών που δείχνουν στις κλάσεις
                        EquivalenceClass* class = (*ptrToClassesArray)[i]; 
                
                        // Εκτύπωσε τον εκπρόσωπο
                        for (size_t j = 0; j < strlen(class->representative); j++) 
                        {
                            printf("%c", class->representative[j]);
                        }
                        printf(" %3d", class->wordCount); // Εκτύπως το πλήθος λέξεων

                        // Ταξινομώ τις λέξεις λεξικογραφικά
                        qsort(class->words, class->wordCount, sizeof(char*), compareWords);

                        // Εκτύπωσε τις λέξεις
                        for (int j = 0; j < class->wordCount; j++) 
                        {
                            printf(" %s", class->words[j]);
                        }
                        printf("\n");
                    }

                    if (chosenClass == NULL) exit(EXIT_FAILURE);
                    printf("\n== CHOSEN ==\n");
                    // Εκτύπωσε τον εκπρόσωπο
                    for (size_t j = 0; j < strlen(chosenClass->representative); j++) 
                    {
                        printf("%c", chosenClass->representative[j]);
                    }

                    printf(" %3d", chosenClass->wordCount); // Εκτύπωσε το πλήθος λέξεων της
                    // Εκτύπωσε τις λέξεις της
                    for (int j = 0; j < chosenClass->wordCount; j++)
                    {
                        printf(" %s", chosenClass->words[j]);
                    }
                    printf("\n");

                #endif

                currentRound++; // Πήγαινε στον επόμενο γύρο
                currentClass = chosenClass;

                bool winConditionMet = true;
                for (size_t i = 0; currentClass->representative[i] != '\0'; i++) 
                {
                    if (currentClass->representative[i] == '_') 
                    {
                        winConditionMet = false;
                        break;
                    }
                }

                if (winConditionMet) 
                {
                    printf("\nYou won! The word is %s.\n", currentClass->words[0]);
                    playerScore++;
                    printf("\n\nYOU: %d - ME: %d\n\n", playerScore, computerScore);
                    gameWon = true;
                    break;
                }
            }
            
        }

        if(currentRound > turns && !gameWon)
        {
            // Ο παίκτης δεν πρόλαβε να μαντέψει τη λέξη και τελείωσαν οι προσπάθειες του
            computerScore++; // Αυξάνω το σκόρ του υπολογιστή
            printf("\nYou lost! The word is %s.\n", currentClass->words[0]); // Δείξε μια οποιαδήποτε λέξη με τον συγκεκριμένο εκπρόσωπο
            printf("\n\nYOU: %d - ME: %d\n\n", playerScore, computerScore);
        }

        // Αποδεσμεύω όλες τις κλάσεις εκτός απο την επιλεγμένη
        for (int i = 0; i < *classCount; ++i) 
        {
            if ((*ptrToClassesArray)[i] != chosenClass) 
            {
                // Αποδεσμεύω όλα τα στοιχεία των κλάσεων αυτών
                freeClass((*ptrToClassesArray)[i]);
                (*ptrToClassesArray)[i] = NULL;
            }
        }

        // Βάζω τον πίνακα να περιέχει μόνο την επιλεγμένη κλάση
        *classCount = 1;
        *ptrToClassesArray = realloc(*ptrToClassesArray, sizeof(EquivalenceClass*)); // Θέλω να χωράει μόνο ένας δείκτης στον πίνακα πλέον
        if (*ptrToClassesArray == NULL) 
        {
            printf("Memory error.\n");
            exit(EXIT_FAILURE);
        }
        (*ptrToClassesArray)[0] = chosenClass; // Βάζω την επιλεγμένη κλάση πάλι πίσω στον πίνακα
    }
}

int main (int argc, char *argv[]) 
{
    int wordLength, turns;
    char *filename;

    errorCodeT resultOfArg = checkArguments(argc, argv, &wordLength, &turns);

    if(resultOfArg != OK)
    {
        printErrorMsg(EXIT_FAILURE);
        return 1;
    }

    filename = argv[3];
    bool playAgain = false;

    do {
        int totalWords = 0, classCount = 0;
        EquivalenceClass** classes = createDictionary(filename, wordLength, &totalWords);
        if (classes == NULL || totalWords == 0) 
        {
            printf("No words of length %d.\n", wordLength);
            return 1;
        }
        classCount = totalWords;
        roundOfGame(&classes, totalWords, turns, &classCount);
        // Αποδεσμεύω τη μνήμη του παιχνιδιού
        freeClassesArray(&classes, classCount);
        playAgain = askToPlayAgain();
    } while (playAgain);


    return 0;
}

我不知道该怎么办

c memory error-handling address-sanitizer
1个回答
0
投票

updateEquivalenceClasses 
你有:

        EquivalenceClass* currentClass = (*ptrArray)[i];

...
...
...

        freeClass(currentClass);   <------ First call freeClass
        currentClass = NULL;
        removeClassFromArray(ptrArray, classCount, i); <--- then call removeClassFromArray
        --i;
        continue; // Πήγαινε στην επόμενη κλάση

但是在

removeClassFromArray
中你会:

if (!ptrToArray || !*ptrToArray || index < 0 || index >= *classCount) return; // Invalid input

freeClass((*ptrToArray)[index]); <---- Call freeClass

所以你最终会使用同一个指针调用

freeClass
两次。

© www.soinside.com 2019 - 2024. All rights reserved.