编写一个字谜解算器并获取分段错误以释放堆内存

Writing a word puzzle solver and getting segmentation fault freeing heap memory

我在堆内存方面遇到了一些问题。我正在用 C 编写一些代码来解决单词谜题——(来自英国的一个名为 Countdown 的游戏)。游戏是给你 9 个字母,目标是尽可能拼出最长的单词。

如果我注释掉对 free() 的所有调用,我的代码就可以工作(尽管我还没有完全测试它),但是当我将 free 调用放入时,我会遇到分段错误。我有 运行 valgrind 并收到类似 "Address 0x9cbe627 is 7 bytes inside a block of size 9 free'd" 和 "invalid read of size 1." 的错误消息

任何人都可以看看并解释发生了什么事吗? (下面给出代码)我将不胜感激!

/**
 * Program to solve word puzzles from the TV show Countdown. Asks for 9 
 * letters for input and then prints out the
 * maximum score and an example word of that score
 */

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

/*
 uses recursion to iterate over combinations of size "length" from 
 data[] of size "lengthData." Stores combinations generated in
 combination[] and permutations in permutation[]. For each combination  
 calls checkPermutations to iterate over all permutations
 of the combination checking against a dictionary for word matches   
 returning true if a match is found
 */
bool checkForWords(char data[], int lengthData, int length, char  
                   combination[], int lengthCombination, char permutation[]);

/**
  * takes in an array - (in practice combination[] from main()) and  
  * iterates through all the permutations of the array storing
  * generated permutations in permutation[] by using recursion. For   
  * each checks against a dictionary for word match. Returns true
  * when match is found
  */
bool checkPermutations(char data[], int lengthData, char permutation[],  
                       int lengthPermutation);

/**
 * takes an array of some length and an index and then retruns a  
 * pointer to an array on the heap that is the same but with the
 * elements at the index and to the left removed and then remaining 
 * elements "shifted down"
 */
char* removeLeftOfIndex(char data[], int lengthData, int index);

/**
 * takes an array of some length and index position and returns a    
 * pointer to an array on the heap that has the element removed
 * and remaining elements "shifted down"
 */
char* removeElementAtIndex(char data[], int lengthData, int index);

int main(void)
{
char letters[9];

// getting letters
for (int i = 0; i < 9; i++)
{
    printf("Please enter letter %i: ", i + 1);
    letters[i] = GetChar();
}

// formatting
printf("\n");


// iterating over the length of word to look for starting at 9 and  
   decrementing
for (int i = 9; i > 0; i--)
{
    char* data = malloc(9 * sizeof(char));
    char* permutation = malloc(10 * sizeof(char));
    char* combination = malloc(9 * sizeof(char));

    for (int j = 0; j < 9; j++)
        data[j] = letters[j];

    // checks to see if there is a word of length i
    if (checkForWords(data, 9, i, combination, i, permutation) == true)
    {   
        printf("Max score: %i\nExample word: %s\n", i, permutation);
        free(permutation);
        return 0;
    }

    free(permutation);
}

// no words found
printf("Max score: 0\nNo words can be made\n");
return 0;
}

bool checkForWords(char data[], int lengthData, int length, char  
                   combination[], int lengthCombination, char permutation[])
{   
// base recursive case
if (length == 0)
{   
    free(data);

    // checks for permutations for the fixed combination
    return checkPermutations(combination, lengthCombination, permutation, lengthCombination);
}

else
{   
    // generating combination by fixing one element and the recursively generating and checking
    for (int j = 0; j <= lengthData - length; ++j)
    {   
        // fixes one element
        combination[lengthCombination - length] = data[j];

        // recursive part
        if (checkForWords(removeLeftOfIndex(data, lengthData, j), lengthData - j - 1, length - 1, combination, 
           lengthCombination, permutation) == true)
           {
                free(data);
                return true;
           }
    }

    free(data);
    return false;
}
}

bool checkPermutations(char data[], int lengthData, char permutation[],  int lengthPermutation)
{   

// base recursive case
if (lengthData == 0)
{   
    // null character for printing string later
    permutation[lengthPermutation] = '[=10=]';


    // checking against dictionary - make this much faster with binary search!!!!
    FILE* dictionary= fopen("/usr/share/dict/words", "r");
    char word[15];
    while (fgets(word, sizeof(word), dictionary) != NULL)
    {   
        // checks to see if match
        if (strncmp(permutation, word, lengthPermutation) == 0)
        {   
            fclose(dictionary);
            free(data);
            return true;
        }

    }

    // not in dictionary
    fclose(dictionary);


    free(data);
    return false;

}

else
{   
    // generating permutations and checking for words by fixing one element and then using recursion.
    for (int j = 0; j < lengthData; j++)
    {   
        // fixing element
        permutation[lengthPermutation - lengthData] = data[j];

        // recursive part
        if (checkPermutations(removeElementAtIndex(data, lengthData, j), lengthData - 1, permutation, lengthPermutation) == true)
        {
            free(data);
            return true;
        }

    }
    free(data);
    return false;
}
}

char* removeLeftOfIndex(char data[], int lengthData, int index)
{   
// allocating heap memory
char* newData = malloc((lengthData - index - 1) * sizeof(char));

// loop to copy relevant parts
for (int j = 0; j < lengthData - index - 1; j++)
    newData[j] = data[j + index + 1];

return newData;
}

char* removeElementAtIndex(char data[], int lengthData, int index)
{   
// allocating heap memory
char* newData = malloc((lengthData - 1) * sizeof(char));

// loops to copy relevant parts
for (int i = 0; i < index; i++)
    newData[i] = data[i];
for(int i = index; i < lengthData - 1; i++)
    newData[i] = data[i + 1];

return newData;

}

可能还值得一提的是,当我尝试调试时,在代码中最后一个函数的实现中调用 malloc() 时似乎发生了分段错误:

// allocating heap memory
char* newData = malloc((lengthData - 1) * sizeof(char));

您的代码是内存分配和释放的蜘蛛网。我对其进行了重新设计,因此内存分配更有意义(无论如何对我来说。)让其调用者的子例程释放内存是奇怪的 - 相反是好的,让调用者释放 return 结果一个子程序。

尽管基本情况(字典中的单词完全匹配)似乎可行,但我不能说我能够测试其他可能性,因此此代码可能需要更多调试。但它并没有抱怨内存错误:

/**
 * Program to solve word puzzles from the TV show Countdown. 
 * Asks for 9 letters for input and then prints out the
 * maximum score and an example word of that score
 */

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

/**
 uses recursion to iterate over combinations of size "length" from 
 data[] of size "lengthData." Stores combinations generated in
 combination[] and permutations in permutation[]. For each combination
 calls checkPermutations to iterate over all permutations
 of the combination checking against a dictionary for word matches 
 returning true if a match is found
 */
bool checkForWords(char data[], int lengthData, int length, char combination[], int lengthCombination, char permutation[]);

/**
 * takes in an array - (in practice combination[] from main()) and
 * iterates through all the permutations of the array storing
 * generated permutations in permutation[] by using recursion. For 
 * each checks against a dictionary for word match. Returns true
 * when match is found
 */
bool checkPermutations(char data[], int lengthData, char permutation[], int lengthPermutation);

/**
 * takes an array of some length and an index and then returns a
 * pointer to an array on the heap that is the same but with the
 * elements at the index and to the left removed and then remaining 
 * elements "shifted down"
 */
char *removeLeftOfIndex(char data[], int lengthData, int index);

/**
 * takes an array of some length and index position and returns a
 * pointer to an array on the heap that has the element removed
 * and remaining elements "shifted down"
 */
char *removeElementAtIndex(char data[], int lengthData, int index);

#define LETTER_COUNT (9)

int main(void)
{
    char letters[LETTER_COUNT];

    // getting letters
    for (int i = 0; i < LETTER_COUNT; i++)
    {
        printf("Please enter letter %i: ", i + 1);
        letters[i] = GetChar();
    }

    // formatting
    printf("\n");

    bool success = false;

    char *data = malloc(LETTER_COUNT);
    char *permutation = malloc(LETTER_COUNT + 1);
    char *combination = malloc(LETTER_COUNT);

    // iterating over the length of word to look for starting at 9 and decrementing
    for (int i = LETTER_COUNT; i > 0; i--)
    {
        (void) strncpy(data, letters, LETTER_COUNT);

        // checks to see if there is a word of length i
        if (checkForWords(data, LETTER_COUNT, i, combination, i, permutation))
        {
            printf("Max score: %i\nExample word: %s\n", i, permutation);
            success = true;
            break;
        }
    }

    if (!success)
    {
        printf("Max score: 0\nNo words can be made\n");
    }

    free(data);
    free(permutation);
    free(combination);

    return EXIT_SUCCESS;
}

bool checkForWords(char data[], int lengthData, int length, char combination[], int lengthCombination, char permutation[])
{
    // base recursive case
    if (length == 0)
    { 
        // checks for permutations for the fixed combination
        return checkPermutations(combination, lengthCombination, permutation, lengthCombination);
    }

    // generating combination by fixing one element and the recursively generating and checking
    for (int j = 0; j <= lengthData - length; ++j)
    { 
        // fixes one element
        combination[lengthCombination - length] = data[j];

        // recursive part
        char *reduced = removeLeftOfIndex(data, lengthData, j);
        if (checkForWords(reduced, lengthData - j - 1, length - 1, combination, lengthCombination, permutation))
        {
            free(reduced);
            return true;
        }
        free(reduced);
    }

    return false;
}

bool checkPermutations(char data[], int lengthData, char permutation[], int lengthPermutation)
{
    // base recursive case
    if (lengthData == 0)
    { 
        // null character for printing string later
        permutation[lengthPermutation] = '[=10=]';

        // checking against dictionary - make this much faster with binary search!!!!
        FILE* dictionary= fopen("/usr/share/dict/words", "r");
        char word[64];

        while (fgets(word, sizeof(word), dictionary) != NULL)
        { 
            // checks to see if match
            if (strncmp(permutation, word, lengthPermutation) == 0)
            { 
                fclose(dictionary);
                return true;
            }
        }

        // not in dictionary
        fclose(dictionary);
        return false;
    }

    // generating permutations and checking for words by fixing one element and then using recursion.
    for (int j = 0; j < lengthData; j++)
    { 
        // fixing element
        permutation[lengthPermutation - lengthData] = data[j];

        // recursive part
        char *reduced = removeElementAtIndex(data, lengthData, j);
        if (checkPermutations(reduced, lengthData - 1, permutation, lengthPermutation))
        {
            free(reduced);
            return true;
        }
        free(reduced);
    }

    return false;
}

char *removeLeftOfIndex(char data[], int lengthData, int index)
{ 
    // allocating heap memory
    char *newData = malloc(lengthData - index - 1);

    // loop to copy relevant parts
    for (int j = 0; j < lengthData - index - 1; j++)
    {
        newData[j] = data[j + index + 1];
    }

    return newData;
}

char *removeElementAtIndex(char data[], int lengthData, int index)
{ 
    // allocating heap memory
    char *newData = malloc(lengthData - 1);

    // loops to copy relevant parts
    for (int i = 0; i < index; i++)
    {
        newData[i] = data[i];
    }

    for (int i = index; i < lengthData - 1; i++)
    {
        newData[i] = data[i + 1];
    }

    return newData;
}

祝你好运,希望对你有所帮助!