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

发布于 2025-01-11 12:20:43 字数 7248 浏览 0 评论 0原文

我一直在开发一个项目,该项目将整个文件作为单个字符串并以各种方式操作它们,但是当运行大于大约 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] = '\0';

    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] = '\0';

    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] != '\0'; i++)
    {
        if(checkPunctuation(aStringToShrink[i]) == 1)
        {
            puncCount++;
        }
        else
        {
            tempShrinked[tempIndex] = aStringToShrink[i];
            tempIndex++;
        }
    }

    tempShrinked[tempIndex] = '\0';
    
    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 分配的空间)< /em>

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

Ive been working on a project which takes whole files as single strings and manipulates them in various ways, but keep getting stuck on a valgrind error when running text files bigger than around ~500 characters. Some code for reference:

My Program:

#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] = '\0';

    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] = '\0';

    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] != '\0'; i++)
    {
        if(checkPunctuation(aStringToShrink[i]) == 1)
        {
            puncCount++;
        }
        else
        {
            tempShrinked[tempIndex] = aStringToShrink[i];
            tempIndex++;
        }
    }

    tempShrinked[tempIndex] = '\0';
    
    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;
    }
}

I get no errors when calling readFile(); by itself, it allocates and frees fine. It is only when it is a larger file and the function splitMe(); is called, Valgrind reports 2 errors:

==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)

(The 733 bytes is the space allocated by that first calloc in readFile)

Im assuming maybe it has something to do with a combination of the calloc(); in readFile and the strcpy(); in splitMe? Any help is appreciated. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

樱花落人离去 2025-01-18 12:20:43

首先,您做出了一个巨大的假设:您可以将整个文件放入内存中。但不要检查它是否有效。

// 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);

您必须

在 splitME 开始时检查 calloc 的返回,即使该返回有效,您也可以将整个文件复制到另一个堆分配,而无需测试它是否有效。

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_LENGTH,则为 MAX_WORDS

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

Well for a start you make the huge assumption that you can fit the entire file into memory. But dont check that it worked

// 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);

You have to check the return from calloc

then at the start of splitME even if that one worked you copy the entire file to another heap allocation with no test if it worked.

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

so you are attempting to hold 2 copies of the file in memory at once

With a 500 byte file this is probably OK, but this is very poor code

The actual reason you are failing is because you don't check to see if you have > MAX_WORDS of if a word is larger than MAX_LENGTH

Also note that you program won't work on windows. You find the length of the file including all the CRLFs at the end of the line, but open the file in text mode, fread will drop the LFs so your test to see if you read the correct number of chars will fail

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文