仅当通过 C 程序 运行 较大的文本文件时,free() 无效

Invalid free() only when running larger text files through C program

我一直在从事一个项目,该项目将整个文件作为单个字符串并以各种方式对其进行操作,但是当 运行 文本文件大于约 500 个字符时,我一直陷入 valgrind 错误。部分代码供参考:

我的程序:

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

#define MAX_LENGTH 20
#define MAX_WORDS 50

// REQUIRED PROTOTYPES

char * readFile (char * filename);

char * stretchMe (char * aStringToStretch);

int splitMe (char * aStringToSplit, char static2D [MAX_WORDS][MAX_LENGTH]);

int shrinkMe (char * aStringToShrink);

bool isItAPalindrome (char * aString);

void printSuffixes (char * aString, int whichWord, char * desiredSuffix);

// Custom Functions

int checkPunctuation(char x);

// Main

int main(int argc, char **argvs)
{ 
    if(argc < 2)
    {
        puts("Wrong usage when executing");
        exit(EXIT_FAILURE);
    }
    
    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    printf("Txt File: [%s]\n", argvs[1]);
    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    char *ioFileString;
    ioFileString = readFile(argvs[1]);
    printf("%s", ioFileString);

    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    /*
    char *stretchedIoFileString;
    stretchedIoFileString = stretchMe(ioFileString);
    printf("%s", stretchedIoFileString);
    free(stretchedIoFileString);
    */
    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    char static2D [MAX_WORDS][MAX_LENGTH];
    int wordsCounted = splitMe(ioFileString, static2D);
    printf("Word Count :[%d]", wordsCounted);

    puts("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    free(ioFileString);
    return EXIT_SUCCESS;
}

char * readFile (char * filename)
{
    FILE *fp = NULL; // Initialize file pointer

    fp = fopen(filename, "r"); // Open file

    if(fp == NULL) // Check if file was found
    {
        printf("Error: Could not find file %s, please try again", filename);
        exit(-1); // Error
    }

    // First count number of characters in file
    fseek(fp, 0, SEEK_END); // Seek to end of file
    int cCount = ftell(fp); // Counts amount of characters in file, add one for endline.
    fseek(fp, 0, SEEK_SET); // Seek back to the beginning of the file

    char *buffer = calloc((cCount+1), sizeof(char));

    if(buffer == NULL)
    {
        puts("Malloc Failed, exiting");
        exit(EXIT_FAILURE);
    }

    int numRead = fread(buffer, sizeof(char), cCount, fp);

    buffer[cCount] = '[=11=]';

    if(numRead != cCount)
    {
        puts("Did not read correctly, exiting.");
        exit(EXIT_FAILURE);
    }

    fclose(fp);

    return buffer;
}

char * stretchMe (char * aStringToStretch)
{
    const int stringLength = strlen(aStringToStretch);

    int *userInput = calloc(stringLength, sizeof(int));

    int newStringLength = 0;

    printf("Please enter %d integers sequentially:\n", stringLength);

    int inUser;

    for (int i = 0; i < stringLength; i++)
    {
        //scanf("%d", &inUser);

        inUser = 2;

        userInput[i] = inUser;

        if(userInput[i] < 1)
        {
            printf("\nInvalid value: values must be positive\n");
            i--;
        }
        else
        {
            newStringLength = newStringLength + userInput[i];
        }
    }

    char *stretchedString = malloc(sizeof(char)*(newStringLength + 1));

    int index = 0;

    for (int i  = 0; i < stringLength; i++)
    {
        for(int j = 0; j < userInput[i]; j++)
        {
            stretchedString[index] = aStringToStretch[i];
            index++;
        }
    }

    stretchedString[index] = '[=11=]';

    free(userInput);

    return stretchedString;
}

int splitMe (char * aStringToSplit, char static2D [MAX_WORDS][MAX_LENGTH])
{
    const int stringLength = strlen(aStringToSplit);
    const char delim[] = " \n";

    char *buffer = calloc(stringLength+1, sizeof(char)); // Alloc memory for buffer for strtok();
    strcpy(buffer, aStringToSplit); // Copy string to buffer

    char *token;
    token = strtok(buffer, delim);

    int wordCount = 0;
    while(token != NULL)
    {
        puts("Loops");
        printf("%d", wordCount);
        strcpy(static2D[wordCount], buffer);
        wordCount++;

        token = strtok(NULL, delim);
    }

    free(buffer);

    return wordCount;
}

/*int shrinkMe (char * aStringToShrink)
{
    int puncCount = 0;
    int tempIndex = 0;

    int stringLength = strlen(aStringToShrink);

    char *tempShrinked = malloc(sizeof(char)*stringLength);

    for(int i = 0; aStringToShrink[i] != '[=11=]'; i++)
    {
        if(checkPunctuation(aStringToShrink[i]) == 1)
        {
            puncCount++;
        }
        else
        {
            tempShrinked[tempIndex] = aStringToShrink[i];
            tempIndex++;
        }
    }

    tempShrinked[tempIndex] = '[=11=]';
    
    strcpy(aStringToShrink, tempShrinked);

    printf("%s", tempShrinked);
    printf("%s", aStringToShrink);
    return puncCount;
}

bool isItAPalindrome (char * aString)
{
    return true;
}

void printSuffixes (char * aString, int whichWord, char * desiredSuffix)
{

}*/

int checkPunctuation(char x)
{
    switch (x)
    {  
    case '.':
    case ':':
    case ';':
    case '?':
    case '!':
        return 1; // If any of the above cases are found, the case flows down the line to the last
        break;

    default:
        return 0;
        break;
    }
}

我自己调用 readFile(); 时没有出错,它分配和释放都很好。只有当文件比较大,调用函数splitMe();时,Valgrind报2个错误:

==19545== Invalid free() / delete / delete[] / realloc()
==19545==    at 0x48369AB: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==19545==    by 0x109335: main (main.c:35)
==19545==  Address 0x65685404a26730 is not stack'd, malloc'd or (recently) free'd
==19545== 
==19545== 
==19545== HEAP SUMMARY:
==19545==     in use at exit: 733 bytes in 1 blocks
==19545==   total heap usage: 7 allocs, 7 frees, 15,627 bytes allocated
==19545== 
==19545== Searching for pointers to 1 not-freed blocks
==19545== Checked 67,600 bytes
==19545== 
==19545== 733 bytes in 1 blocks are definitely lost in loss record 1 of 1
==19545==    at 0x4837B65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==19545==    by 0x1093E0: readFile (functions.c:24)
==19545==    by 0x10928B: main (main.c:17)
==19545== 
==19545== LEAK SUMMARY:
==19545==    definitely lost: 733 bytes in 1 blocks
==19545==    indirectly lost: 0 bytes in 0 blocks
==19545==      possibly lost: 0 bytes in 0 blocks
==19545==    still reachable: 0 bytes in 0 blocks
==19545==         suppressed: 0 bytes in 0 blocks
==19545== 
==19545== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==19545== 
==19545== 1 errors in context 1 of 2:
==19545== Invalid free() / delete / delete[] / realloc()
==19545==    at 0x48369AB: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==19545==    by 0x109335: main (main.c:35)
==19545==  Address 0x65685404a26730 is not stack'd, malloc'd or (recently) free'd
==19545== 
==19545== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

(733字节是readFile中第一个calloc分配的space)

我假设它可能与 readFile 中的 calloc(); 和 splitMe 中的 strcpy(); 的组合有关?任何帮助表示赞赏。谢谢。

首先,您假设可以将整个文件装入内存。但是不要检查它是否有效

// First count number of characters in file
fseek(fp, 0, SEEK_END); // Seek to end of file
int cCount = ftell(fp); // Counts amount of characters in file, add one for endline.
fseek(fp, 0, SEEK_SET); // Seek back to the beginning of the file


char* buffer = calloc((cCount + 1), sizeof(char));

int numRead = fread(buffer, sizeof(char), cCount, fp);

你必须检查来自 calloc

的 return

然后在 splitME 开始时,即使它有效,您也将整个文件复制到另一个堆分配,而无需测试它是否有效。

const int stringLength = strlen(aStringToSplit);
const char delim[] = " \n";

char* buffer = calloc(stringLength + 1, sizeof(char)); // Alloc memory for buffer for strtok(); <<<<=== check this retunrn
strcpy(buffer, aStringToSplit); // Copy string to buffer

所以您正试图在内存中同时保存文件的 2 个副本

对于 500 字节的文件,这可能没问题,但这是非常糟糕的代码

你失败的真正原因是你没有检查是否有 > MAX_WORDS 或一个词是否大于 MAX_LENGTH

另请注意,您的程序无法在 windows 上运行。您发现文件的长度包括行尾的所有 CRLF,但是以文本模式打开文件,fread 将删除 LF,因此您测试是否读取了正确数量的字符将失败