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