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.
CodePudding user response:
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