Realloc() causes leaks that I cannot find - c

The issue:
I have functions (add_filename() and init_filename()) that I'm currently testing. They have to read lines from a file into a dynamically allocated array of strings. Init starts the array, and Add adds new elements. Both of these functions return a pointer to the beginning of the array. I have taken all the necessary precautions to make sure realloc() does not lose the pointer. There are also flags that indicate that something is wrong with the memory allocation. And I free everything (that i can think of). Yet, it is still leaking...
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **add_filename(char **filenames, char *new_file, int *file_num, int *flag);
char **init_filename(char *new_file, int *flag);
int main() {
FILE *file;
file = fopen("file.txt", "r");
char *buffer = 0;
size_t buf_size = 0;
size_t chars = 0;
int file_num = 0, check = 1;
// char ch;
if (file != NULL) {
char **files;
while ((int)(chars = getline(&buffer, &buf_size, file)) > 0) {
if (!file_num) {
files = init_filename(buffer, &check);
file_num++;
}
files = add_filename(files, buffer, &file_num, &check);
printf("files = %s", files[file_num - 1]);
free(buffer);
buffer = NULL;
if (check == 0) {
printf("we have problems\n");
break;
}
}
free(buffer);
buffer = NULL;
fclose(file);
if (files) {
for (int i = 0; i < file_num; i++) {
free(files[i]);
}
}
}
return 0;
}
char **init_filename(char *new_file, int *flag) {
char **init = malloc((1) * sizeof(char*)); //
if (init) {
init[0] = malloc((strlen(new_file) + 1) * sizeof(char));
if (!init[0])
*flag = 0;
} else {
*flag = 0;
}
return init;
}
char **add_filename(char **filenames, char *new_file, int *file_num, int *flag) {
char **temp = realloc(filenames, (*file_num + 1) * sizeof(char *));
if (temp) {
filenames = temp;
filenames[*file_num] = malloc((strlen(new_file) + 1) * sizeof(char));
if (filenames[*file_num] != NULL) {
strcpy(filenames[*file_num], new_file);
*file_num = *file_num + 1;
} else {
*flag = 0;
}
} else {
*flag = 0;
}
return filenames;
}
This is the output of valgrind:
==5881== HEAP SUMMARY:
==5881== in use at exit: 32 bytes in 1 blocks
==5881== total heap usage: 15 allocs, 14 frees, 6,285 bytes allocated
==5881==
==5881== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1
==5881== at 0x484DCD3: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==5881== by 0x1094E2: add_filename (test.c:72)
==5881== by 0x109335: main (test.c:23)
==5881==
==5881== LEAK SUMMARY:
==5881== definitely lost: 32 bytes in 1 blocks
==5881== indirectly lost: 0 bytes in 0 blocks
==5881== possibly lost: 0 bytes in 0 blocks
==5881== still reachable: 0 bytes in 0 blocks
==5881== suppressed: 0 bytes in 0 blocks
==5881==
==5881== For lists of detected and suppressed errors, rerun with: -s
==5881== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
And this is the output of ASan:
Direct leak of 32 byte(s) in 1 object(s) allocated from:
#0 0x7f200c4b4c18 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:164
#1 0x5591559a2a8a in add_filename /home/licht/Documents/Knowledge/school21/inProgress/C3_SimpleBashUtils-0/src/test.c:72
#2 0x5591559a2631 in main /home/licht/Documents/Knowledge/school21/inProgress/C3_SimpleBashUtils-0/src/test.c:23
#3 0x7f200c029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
SUMMARY: AddressSanitizer: 32 byte(s) leaked in 1 allocation(s).

There are multiple problems in the code:
files must be initialized to NULL: it will be initialized by init_filename if the file is not empty but will remain uninitialized and cause undefined behavior when freed at the end of main() (if files is finally freed as it should be).
you should strip the trailing newline left by getline().
There is no need for init_filename(): passing a null pointer to realloc() is allowed and realloc will behave like malloc in this case.
the first filename is added twice in the array: once by init_filename and another time by add_filename
there is no need to free buffer inside the loop: it will be reused for the next line and reallocated as needed.
allocating a copy of a string should be done with strdup() which safely combines memory allocation and string copy in a single call. This function is part of the C23 C Standard and has been available for decades on POSIX systems. It is easy to implement on legacy systems that do not provide it. There is no doubt it is available on your system since you rely on getline.
last but not least: you forget to free files, which is probably the leak you observe.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **add_filename(char **filenames, const char *new_file,
int *file_num, int *flag);
int main() {
FILE *file;
char *buffer = NULL;
size_t buf_size = 0;
char **files = NULL;
int file_num = 0, check = 1;
file = fopen("file.txt", "r");
if (file == NULL) {
fprintf(stderr, "cannot open %s: %s\n", "file.txt", strerror(errno));
return 1;
}
while (getline(&buffer, &buf_size, file) > 0) {
// clear the trailing newline if any
buffer[strcspn(buffer, "\n")] = '\0';
files = add_filename(files, buffer, &file_num, &check);
if (check == 0) {
printf("filename could not be inserted: %s\n", buffer);
break;
}
printf("file = %s\n", files[file_num - 1]);
}
free(buffer);
fclose(file);
if (files) {
for (int i = 0; i < file_num; i++) {
free(files[i]);
}
free(files);
}
return 0;
}
char **add_filename(char **filenames, const char *new_file,
int *file_num, int *flag) {
int num = *file_num;
char **temp = realloc(filenames, (num + 1) * sizeof(*filenames));
if (temp) {
filenames = temp;
filenames[num] = strdup(new_file);
if (filenames[num] != NULL) {
// update the filename count and set the success flag
*file_num = num + 1;
*flag = 1;
} else {
// could not allocate new string: clear flag
*flag = 0;
}
} else {
// realloc failed: clear flag to indicate failure
*flag = 0;
}
return filenames;
}

Related

how to free memory in c in this context

context: Create a student record using structures and pointers in C language
#define MAXS 200
typedef struct student{
int id;
char *name;
int age;
float gpa;
}student;
int main(int argc,char **argv)
{
if(argc < 2){
fprintf(stderr,"ERROR: filename csv. please\n");
exit(EXIT_FAILURE);
}
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* characters read by getline () */
size_t n = 0; /* number of bytes to allocate */
char *p = NULL; /* pointer to use parsing line */
char *sp = NULL; /* 2nd pointer to use parsing line */
int field = 0;
int cnt = 0; /* counter for number allocated */
student **students = NULL; /* ptr to array of stuct student */
int it = 0;
FILE *fp = fopen(argv[1],"r");
if(!fp){
fprintf(stderr,"failed to open file for reading\n");
exit(EXIT_FAILURE);
}
students = calloc(MAXS, sizeof(*students));
if(!students){
fprintf(stderr,"calloc failed to allocate\n");
exit(EXIT_FAILURE);
}
// 2158,John Q. Student,11,88.42
while((read = getline(&line,&n,fp)) != -1){
sp = p = line;
field = 0;
students[cnt] = malloc(sizeof(**students));
if(!students[cnt]){
fprintf(stderr,"malloc failed to allocate students\n");
exit(EXIT_FAILURE);
}
while(*p){
if(*p == ',')
{
*p = 0;
if(field == 0) students[cnt]->id = atoi(sp);
if(field == 1) students[cnt]->name = strdup(sp);
if(field == 2) students[cnt]->age = atoi(sp);
*p = ',';
sp = p+1;
field++;
}
p++;
}
students[cnt]->gpa = strtof(sp,NULL);
cnt ++;
if(cnt == MAXS){
fprintf(stderr,"ERROR: MAXS reached\n");
break;
}
}
printf("cnt = %d\n",cnt);
/* iterate over all students and print */
printf("\nThe students in the class are: \n\n");
while(students[it]){
printf(" %d %s %d %6.2f\n"
,students[it]->id,students[it]->name,students[it]->age,students[it]->gpa);
it++;
}
int total = it;
printf("\nTotal number of students: %d\n",total);
/* compute average age and gpa*/
float avg_age = 0;
float avg_gpa = 0;
for(it = 0; it < total; it++)
{
avg_age += (float) students[it]->age;
avg_gpa += (float) students[it]->gpa;
}
avg_age /= (float) total;
avg_gpa /= (float) total;
printf("Average Age of students: %.2f\n",avg_age);
printf("Average GPA of students: %.2f\n",avg_gpa);
for(it = 0; it < MAXS; it++){
free(students[it]);
}
free(students);
exit(EXIT_SUCCESS);
}
valgrind command output:
==567070==
==567070== HEAP SUMMARY:
==567070== in use at exit: 672 bytes in 7 blocks
==567070== total heap usage: 15 allocs, 8 frees, 7,512 bytes allocated
==567070==
==567070== 80 bytes in 5 blocks are definitely lost in loss record 1 of 3
==567070== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==567070== by 0x491560E: strdup (strdup.c:42)
==567070== by 0x1094B4: main (in /home/jian/helloc/a.out)
==567070==
==567070== 120 bytes in 1 blocks are still reachable in loss record 2 of 3
==567070== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==567070== by 0x48ED1A2: getdelim (iogetdelim.c:62)
==567070== by 0x109261: main (in /home/jian/helloc/a.out)
==567070==
==567070== 472 bytes in 1 blocks are still reachable in loss record 3 of 3
==567070== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==567070== by 0x48EC6CD: __fopen_internal (iofopen.c:65)
==567070== by 0x48EC6CD: fopen##GLIBC_2.2.5 (iofopen.c:86)
==567070== by 0x10920C: main (in /home/jian/helloc/a.out)
==567070==
==567070== LEAK SUMMARY:
==567070== definitely lost: 80 bytes in 5 blocks
==567070== indirectly lost: 0 bytes in 0 blocks
==567070== possibly lost: 0 bytes in 0 blocks
==567070== still reachable: 592 bytes in 2 blocks
==567070== suppressed: 0 bytes in 0 blocks
==567070==
==567070== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
If i PUT free(students[it]->name); In the last for loop, then it will Segmentation fault (core dumped) . How can I free all the memory in this context?
Changed MAXS to cnt in the loop where you free students, and added a free of the name.
for(it = 0; it < cnt; it++){
free(students[it]->name);
free(students[it]);
}
getline() allocates line, so free that in loop and each exit point.
Initialized name in case the field is missing (#SupportUkraine).
fclose(fp) after the loop that reads the file
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXS 100
typedef struct student{
int id;
char *name;
int age;
float gpa;
}student;
int main(int argc,char **argv)
{
if(argc < 2){
fprintf(stderr,"ERROR: filename csv. please\n");
exit(EXIT_FAILURE);
}
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* characters read by getline () */
size_t n = 0; /* number of bytes to allocate */
char *p = NULL; /* pointer to use parsing line */
char *sp = NULL; /* 2nd pointer to use parsing line */
int field = 0;
int cnt = 0; /* counter for number allocated */
student **students = NULL; /* ptr to array of stuct student */
int it = 0;
FILE *fp = fopen(argv[1],"r");
if(!fp){
fprintf(stderr,"failed to open file for reading\n");
exit(EXIT_FAILURE);
}
students = calloc(MAXS, sizeof(*students));
if(!students){
fprintf(stderr,"calloc failed to allocate\n");
exit(EXIT_FAILURE);
}
// 2158,John Q. Student,11,88.42
while((read = getline(&line,&n,fp)) != -1){
sp = p = line;
field = 0;
students[cnt] = malloc(sizeof(**students));
if(!students[cnt]){
fprintf(stderr,"malloc failed to allocate students\n");
free(line);
exit(EXIT_FAILURE);
}
students[cnt]->name = NULL;
while(*p){
if(*p == ',')
{
*p = 0;
if(field == 0) students[cnt]->id = atoi(sp);
if(field == 1) students[cnt]->name = strdup(sp);
if(field == 2) students[cnt]->age = atoi(sp);
*p = ',';
sp = p+1;
field++;
}
p++;
}
students[cnt]->gpa = strtof(sp,NULL);
cnt ++;
if(cnt == MAXS){
fprintf(stderr,"ERROR: MAXS reached\n");
break;
}
}
fclose(fp);
free(line);
printf("cnt = %d\n",cnt);
/* iterate over all students and print */
printf("\nThe students in the class are: \n\n");
while(students[it]){
printf(" %d %s %d %6.2f\n"
,students[it]->id,students[it]->name,students[it]->age,students[it]->gpa);
it++;
}
int total = it;
printf("\nTotal number of students: %d\n",total);
/* compute average age and gpa*/
float avg_age = 0;
float avg_gpa = 0;
for(it = 0; it < total; it++)
{
avg_age += (float) students[it]->age;
avg_gpa += (float) students[it]->gpa;
}
avg_age /= (float) total;
avg_gpa /= (float) total;
printf("Average Age of students: %.2f\n",avg_age);
printf("Average GPA of students: %.2f\n",avg_gpa);
for(it = 0; it < cnt; it++){
free(students[it]->name);
free(students[it]);
}
free(students);
exit(EXIT_SUCCESS);
}
and valgrind hates us less:
==1387007== HEAP SUMMARY:
==1387007== in use at exit: 0 bytes in 0 blocks
==1387007== total heap usage: 21 allocs, 28 frees, 6,798 bytes allocated
==1387007==
==1387007== All heap blocks were freed -- no leaks are possible

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

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.
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

Getting segmentation fault. Invalid memory write of size 1 on Valgrind (CS50 PSET5 Speller)

Can't seem to make Valgrind happy.
Valgrind Result:
==21003== Invalid write of size 1
==21003== at 0x4C32E0D: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21003== by 0x40125E: load (dictionary.c:111)
==21003== by 0x400964: main (speller.c:40)
Invalid write of size 8
==22741== at 0x40123E: load (dictionary.c:107)
==22741== by 0x400964: main (speller.c:40)
==22741== Address 0x55cd9a0 is 32 bytes before an unallocated block of size 4,183,584 in arena "client"
Problem seems to be in line 110 which is StrCpy in my bool LOAD.
Code as below - I can't understand why there's an invalid write of size 1.
Another one is
n -> next = table[hashInt];
I don't understand why I am getting invalid write.
bool load(const char *dictionary)
{
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
printf("error opening file");
return 1;
}
char word [LENGTH + 1];
while (fscanf(file, "%s\n", word) != EOF)
{
int hashInt = hash(word);
node *n = malloc(sizeof(n));
if (n == NULL)
{
unload();
return 1;
}
if (table[hashInt] == NULL)
{
table[hashInt] = n;
}
else
{
n -> next = table[hashInt];
table[hashInt] = n;
}
strcpy(n -> word, word);
wordLoaded++;
}
fclose(file);
return 0;
}
This node *n = malloc(sizeof(n)); is probably a typo. Did you mean sizeof(node)?

Reading a text file into a character array in c

I'm writing a program that uses the command-line arguments to receive the name of a text file from the user. The text file is a very simple CSV file such as:
Bob's experiment,12,14,15,16
Mary's experiment,16,15,18
I just want it to print the experiment name then the average of all the numerical values. I'm attempting to do this by putting all the numbers and commas into a char array and I don't know where I've gone wrong.
This is what I have:
int main(int argc, char *argv[])
{
if(argc == 2) {
FILE *txt_file;
txt_file=fopen(argv[1], "rt");
char str[4096];
if(!txt_file) {
printf("File does not exist.\n");
return 1;
}
while(!feof(txt_file)){
char s;
s = fgetc(txt_file);
//prints experiment name
if(s != ',' && (!isdigit(s))) {
printf("%c", s);
}
if(isdigit(s) || s == ',') {
fgets(str, 4096, txt_file);
}
}
fclose(txt_file);
return 0;
}
There are a number of ways to do this, but you should tailor your input routine to the type of data you are reading from your file. Here you are reading lines of data, so you should focus on line-oriented input routines (fgets, getline, or a shoehorned scanf). The basic approach is to read a line of input from your file into a buffer and then parse the line as needed. You can do this dynamically allocating all storage needed, or you can define a maximum value that should be large enough to handle your data.
Next you will need to parse the buffer read from the file to get the experiment name and each of the values associated so that an average can be calculated. Again, there are many ways to do this, but strtol is tailor made for this purpose. It takes a pointer to the string to convert and returns an endptr to the next character that is not a number. This allows you to read a values and set pointer = endptr+1 which sets you up to read your next number.
I have put these pieces together in the example below. It is commented to help you follow along. Drop a comment if you have any additional questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXEXPS 256
int main (int argc, char* argv[])
{
if (argc < 2) {
fprintf (stderr, "error: insufficient input. Usage %s <filename>\n", argv[0]);
return 1;
}
char *line = NULL; /* line read from file (getline allocates if NULL) */
size_t n = 0; /* number of characters to read (0 - no limit) */
ssize_t nchr = 0; /* number of characters actually read by getline */
char *p = NULL; /* pointer to use parsing values from line */
char *lnp = NULL; /* second pointer to use parsing values from line */
char *expname[MAXEXPS] = {0}; /* array of MAXEXPS pointers for experiment names */
int expavg[MAXEXPS] = {0}; /* array of MAXEXPS ints to hold averages */
int val = 0; /* val returned by each call to strtol */
int eidx = 0; /* experiment index */
int idx = 0; /* value index */
FILE *txt_file = fopen(argv[1], "r");
if (!txt_file) {
fprintf (stderr, "error: unable to open file '%s'\n", argv[1]);
return 1;
}
while ((nchr = getline (&line, &n, txt_file)) != -1) /* read each line in file */
{
p = strchr (line, ','); /* find first ',' */
*p = 0; /* set it to null (zero) */
expname[eidx] = strdup (line); /* copy exp name to array (strdup allocates) */
lnp = ++p; /* set lnp to next char */
int sum = 0; /* reset sum to 0 */
idx = 0; /* reset idx to 0 */
while ((val = (int)strtol (lnp, &p, 10)) != 0 && lnp != p) /* read next number */
{
sum += val; /* add val to sum */
lnp = ++p; /* set lnp to next char */
idx++; /* inc idx */
}
expavg[eidx++] = (idx > 0) ? sum / idx : 0; /* calc avg for experiment */
}
fclose (txt_file);
/* print the averages of experiments */
n = 0;
printf ("\n Experiment Avg\n");
printf (" -----------------------\n");
while (expname[n])
{
printf (" %-18s %d\n", expname[n], expavg[n]);
n++;
}
printf ("\n");
/* free all allocated memory */
n = 0;
if (line)
free (line);
while (expname[n])
free (expname[n++]);
return 0;
}
output:
$ ./bin/csvavgfixed dat/csvavg.dat
Experiment Avg
-----------------------
Bob's experiment 14
Mary's experiment 16
memory allocation/free summary:
==22148== HEAP SUMMARY:
==22148== in use at exit: 0 bytes in 0 blocks
==22148== total heap usage: 4 allocs, 4 frees, 723 bytes allocated
==22148==
==22148== All heap blocks were freed -- no leaks are possible
==22148==
==22148== For counts of detected and suppressed errors, rerun with: -v
==22148== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
I think this would do what you want.
#include <stdio.h>
int main(int argc, char* argv[])
{
int n = 0, count = 0, t = 0;
if(argc == 2) {
FILE *txt_file;
txt_file=fopen(argv[1], "rt");
char str[4096];
if(!txt_file) {
printf("File does not exist.\n");
return 1;
}
while(!feof(txt_file)){
char s;
s = fgetc(txt_file);
//prints experiment name
if(s != ',' && (!isdigit(s))) {
if(n!=0) {
printf("%d\n", n / count);
n = 0;
count = 0;
}
printf("%c", s);
}
if(s == ',') {
fscanf(txt_file, "%d", &t);
n+=t;
count++;
}
}
printf("%d\n", n / count);
fclose(txt_file);
return 0;
}
}

Copying file names into an array with C

I'm attempting to find all the files in a directory of a certain type (hardcoded here to tif) and copy them into an array. Everything compiles cleanly (gcc -Wall gives no errors or warnings) but there are some memory issues. Though the program I've written seems to run cleanly (no segfaults), some of the file names are weird characters you get when you've got something other than ascii values in your string. This led me to run with valgrind, which shows errors (output below) but I can't track down what the actual problem is. In some directories, valgrind it self segfaults (the program runs clean in the same dir).
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <search.h>
#include <string.h>
#include <error.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
int exitStatus = 0;
/*------------------------------------------------------------------------------
* array_find
*
* ARGS - Takes a pointer to a string, a pointer to an array of strings, and an
* int representing the length of the array.
*
* RETURN - returns an int indicating the first index of the key in the array,
* or -1 if the key was not found
*-----------------------------------------------------------------------------*/
int array_find(char *key, char *argv[], int argc){
int i;
for (i = 0; i < argc; i++)
{
#ifdef DEBUG_array_find
printf("strncmp(%s, %s, %d) = %d\n", key, argv[i], min(strlen(key), strlen(argv[i])), strncmp(key, argv[i], min(strlen(key), strlen(argv[i]))));
#endif
if (strncmp(key, argv[i], min(strlen(key), strlen(argv[i]))) == 0)
{
return i;
}
}
return -1;
}
/*------------------------------------------------------------------------------
* ends_with
*
* ARGS - str = string to be checked
* sub = string to look for
*
* RETURN - Returns true if str ends with sub or both strings are NULL.
False otherwise.
*-----------------------------------------------------------------------------*/
bool ends_with(char *str, char *sub){
if (str == NULL && sub == NULL)
{
return true;
}
if (str == NULL || sub == NULL)
{
return false;
}
char *last_instance_of_sub = rindex(str, *sub); //Finds the last index of the first char of sub
int sub_len = strlen(sub);
if (last_instance_of_sub == NULL || strlen(last_instance_of_sub) != sub_len)
{
return false;
}
return strncmp(last_instance_of_sub, sub, sub_len) == 0;
}
int main(int argc, char *argv[])
{
/*Parse args*/
DIR *dir;
int index = array_find("-d", argv, argc);
char *dirname;
if (index >= 0)
{
dirname = argv[index + 1];
dir = opendir(dirname);
}
else
{
dirname = getcwd(NULL, 0);
if (dirname == NULL)
{
perror("Error getting current directory name.");
exit(1);
}
dir = opendir(dirname);
}
if (dir == NULL)
{
perror(dirname);
exit(1);
}
#ifdef DEBUG_MAIN
printf("dirname = %s\n", dirname);
#endif
int threads = 1;
index = array_find("-t", argv, argc);
if (index >= 0)
{
threads = atoi(argv[index + 1]);
}
#ifdef DEBUG_MAIN
printf("threads = %d\n", threads);
#endif
struct dirent *entry = readdir(dir);
int num_files = 0;
while (entry != NULL)
{
if (ends_with(entry->d_name, ".tif")){
#ifdef DEBUG_MAIN
printf("%s\n", entry->d_name);
#endif
num_files++;
}
entry = readdir(dir);
}
if (closedir(dir) != 0)
{
perror("Failed to close directory.");
}
#ifdef DEBUG_MAIN
printf("Num files = %d\n", num_files);
#endif
dir = opendir(dirname);
if (dir == NULL)
{
perror(dirname);
exit(1);
}
entry = readdir(dir);
char *file_names[num_files];
int i = 0;
for(; entry != NULL; i++)
{
if (ends_with(entry->d_name, ".tif")){
file_names[i] = strdup(entry->d_name);
if (file_names[i] == NULL)
{
perror("Could not create the filename array.\n");
exit(1);
}
}
entry = readdir(dir);
}
/* #ifdef DEBUG_MAIN*/
for (i = 0; i < num_files; i++)
{
printf("%s\n", file_names[i]);
/* free(file_names[i]);*/
}
/* #endif*/
free(dir);
return exitStatus;
}
Valgrind output:
==24488== Memcheck, a memory error detector
==24488== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==24488== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==24488== Command: ./myprogram -d /home/chris/Pictures/Catalinas\ with\ Christie/Processed/
==24488==
dirname = /home/chris/Pictures/Catalinas with Christie/Processed/
threads = 1
cacti2_lzn.tif
DSC_2139_lzn.tif
DSC_1512_lzn.tif
DSC_1296_lzn.tif
DSC_1577_lzn.tif
DSC_1658_lzn.tif
DSC_1293_lzn.tif
DSC_1631_lzn.tif
DSC_1418_lzn.tif
DSC_1315_2crop_lzn.tif
DSC_1377_lzn2crop.tif
DSC_2167_lzn.tif
1981-1985-HDR3_lzn2.tif
DSC_2129_lzn.tif
DSC_1448_lzn.tif
DSC_1607_lzn.tif
DSC_1564_lzn.tif
DSC_2052-DSC_2072_lzn.tif
DSC_1487_lzn.tif
DSC_1591_2_lzn.tif
DSC_2124_lzn.tif
DSC_1622_lzn.tif
DSC_2157_lzn.tif
DSC_1685_lzn.tif
Num files = 24
cacti2_lzn.tif
DSC_2139_lzn.tif
DSC_1512_lzn.tif
DSC_1296_lzn.tif
DSC_1577_lzn.tif
DSC_1658_lzn.tif
==24488== Use of uninitialised value of size 8
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24488== by 0x4EA4ECB: puts (ioputs.c:36)
==24488== by 0x400D52: main (batch-convert.c:161)
==24488==
==24488== Invalid read of size 1
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24488== by 0x4EA4ECB: puts (ioputs.c:36)
==24488== by 0x400D52: main (batch-convert.c:161)
==24488== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==24488==
==24488==
==24488== Process terminating with default action of signal 11 (SIGSEGV)
==24488== Access not within mapped region at address 0x0
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24488== by 0x4EA4ECB: puts (ioputs.c:36)
==24488== by 0x400D52: main (batch-convert.c:161)
==24488== If you believe this happened as a result of a stack
==24488== overflow in your program's main thread (unlikely but
==24488== possible), you can try to increase the size of the
==24488== main thread stack using the --main-stacksize= flag.
==24488== The main thread stack size used in this run was 8388608.
==24488==
==24488== HEAP SUMMARY:
==24488== in use at exit: 33,243 bytes in 25 blocks
==24488== total heap usage: 26 allocs, 1 frees, 66,051 bytes allocated
==24488==
==24488== LEAK SUMMARY:
==24488== definitely lost: 0 bytes in 0 blocks
==24488== indirectly lost: 0 bytes in 0 blocks
==24488== possibly lost: 0 bytes in 0 blocks
==24488== still reachable: 33,243 bytes in 25 blocks
==24488== suppressed: 0 bytes in 0 blocks
==24488== Rerun with --leak-check=full to see details of leaked memory
==24488==
==24488== For counts of detected and suppressed errors, rerun with: -v
==24488== Use --track-origins=yes to see where uninitialised values come from
==24488== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
Segmentation fault (core dumped)
It's been a while since I've used C at all, but as I understand it (from the man pages), strdup should use malloc to allocate memory on the heap for the copy of the string. Before I remembered the strdup function, I had tried to do exactly that manually, and had the same errors. I thought maybe my code was flawed, and thought the strdup function would take care of it, but apparently there is some other issue.
Can anyone tell me what I'm doing wrong?
EDIT 1:
As per requests, I've added the full source of the program. Also, for those saying to check i against num_files, as you'll see, I count the number of tif files ahead of time, so I know the exact number of files that will be copied into the array, thus checking the index isn't necessary.
Also, as a note, the program was compiled with DEBUG_MAIN defined, so anything in an #ifdef DEBUG_MAIN block does run. No other debug flags were defined.
in your code this part for(; entry != NULL; i++) is way too dangerous , for example lets say that the value of num_files is 1000 , what if a given directory contains 1002 entries , then you'll have a problem.
replace it with for(; entry != NULL && i < num_files ; i++)
The problem is that if you have any entries that don't match your pattern (such as the . and .. entries), you skip the corresponding entry in the array. It also means you go writing outside your file_names array. You should only increment i when the file name matches.
Using getcwd() instead of just using . for the current directory works, but is hardly necessary.
Using free(dir) instead of closedir(dir) is an unmitigated disaster.
The command line argument handling is unusual. As originally written, it would accept -delete as equivalent to -d. That's not good style.
#include <assert.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
bool ends_with(char *str, char *sub);
int array_find(char *key, char *argv[], int argc);
int array_find(char *key, char *argv[], int argc)
{
for (int i = 0; i < argc; i++)
{
if (strcmp(key, argv[i]) == 0)
return i;
}
return -1;
}
bool ends_with(char *str, char *sub)
{
if (str == NULL && sub == NULL)
return true;
if (str == NULL || sub == NULL)
return false;
char *last_instance_of_sub = rindex(str, *sub);
size_t sub_len = strlen(sub);
if (last_instance_of_sub == NULL || strlen(last_instance_of_sub) != sub_len)
return false;
return strcmp(last_instance_of_sub, sub) == 0;
}
int main(int argc, char *argv[])
{
int index = array_find("-d", argv, argc);
char *dirname;
if (index >= 0)
{
dirname = argv[index + 1];
}
else
{
dirname = getcwd(NULL, 0);
if (dirname == NULL)
{
perror("Error getting current directory name.");
exit(1);
}
}
DIR *dir = opendir(dirname);
if (dir == NULL)
{
perror(dirname);
exit(1);
}
char suffix[] = ".c";
printf("dirname = %s\n", dirname);
struct dirent *entry;
int num_files = 0;
while ((entry = readdir(dir)) != NULL)
{
if (ends_with(entry->d_name, suffix))
num_files++;
}
if (closedir(dir) != 0)
{
perror("Failed to close directory.");
}
printf("Num files = %d\n", num_files);
dir = opendir(dirname);
if (dir == NULL)
{
perror(dirname);
exit(1);
}
char *file_names[num_files];
int i = 0;
while ((entry = readdir(dir)) != NULL)
{
if (ends_with(entry->d_name, suffix))
{
file_names[i] = strdup(entry->d_name);
if (file_names[i++] == NULL)
{
perror("Could not create the filename array.\n");
exit(1);
}
}
}
assert(i <= num_files);
if (i < num_files)
num_files = i;
for (i = 0; i < num_files; i++)
{
printf("%s\n", file_names[i]);
free(file_names[i]);
}
closedir(dir);
return 0;
}
The index of the array should be checked:
i<num_files

Resources