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
Related
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;
}
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
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
I am trying to read in the a file and store the words into a dynamic char array. Right now I am getting a Segmentation fault (core dumped) error.
I have tried using strdup() and strcpy() still i am getting the same error
char ** array;
int main(int argc, char * argv[]){
int size = 0;
int i;
FILE * file;
char * line;
size_t len;
ssize_t read;
file = fopen("wordsEn.txt", "r");
if(file == NULL){
printf("Error coudl not open wordsEn.txt\n");
return -1;
}
while((read = getline(&line, &len, file)) != -1){
size++;
}
array = (char **) malloc((sizeof(char *) * size));
rewind(file);
i = 0;
while((read = getline(&line, &len, file)) != -1){
//strcpy(array[i], line);
array[i] = strdup(line);
i++;
}
for(i = 0; i < size; i++){
printf("%s", array[i]);
}
}
I am expecting for example array[0] to return the string 'alphabet'
I am getting a Segmentation fault (core dumped) error.
warning to get a newly allocated line through getline each time you have to reset line to NULL and len to 0 each time, for instance :
while(line = NULL, len = 0, (read = getline(&line, &len, file)) != -1){
Note you do not have to read two times the file, you can use malloc then realloc to increase the size of the (really) dynamic array
A Proposal :
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char ** array = malloc(0);
size_t size = 0;
FILE * file;
char * line;
size_t len;
ssize_t read;
file = fopen("wordsEn.txt", "r");
if(file == NULL) {
printf("Error coudl not open wordsEn.txt\n");
return -1;
}
while (line = NULL, len = 0, (read = getline(&line, &len, file)) != -1){
array = realloc(array, (size+1) * sizeof(char *));
array[size++] = line;
}
free(line); /* do not forget to free it */
fclose(file);
for(size_t i = 0; i < size; i++){
printf("%s", array[i]);
}
/* free resources */
for(size_t i = 0; i < size; i++){
free(array[i]);
}
free(array);
return 0;
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall ar.c
pi#raspberrypi:/tmp $ cat wordsEn.txt
turlututu foo
bar
loop
pi#raspberrypi:/tmp $ ./a.out
turlututu foo
bar
loop
pi#raspberrypi:/tmp $
Execution under valgrind :
pi#raspberrypi:/tmp $ valgrind ./a.out
==13161== Memcheck, a memory error detector
==13161== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13161== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==13161== Command: ./a.out
==13161==
turlututu foo
bar
loop
==13161==
==13161== HEAP SUMMARY:
==13161== in use at exit: 0 bytes in 0 blocks
==13161== total heap usage: 11 allocs, 11 frees, 5,976 bytes allocated
==13161==
==13161== All heap blocks were freed -- no leaks are possible
==13161==
==13161== For counts of detected and suppressed errors, rerun with: -v
==13161== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)
I study C.
I'm have simple code:
#include <stdio.h>
#include <unistd.h>
unsigned
my_fopen(FILE **fp, const char *file_name)
{
*fp = fopen(file_name, "a"); // memleak here
if (!*fp) {
printf("Can't fopen file: %s\n", file_name);
return 0;
} else {
return 1;
}
}
int
main(int argc, char **argv)
{
// begin only for -Wall -Werror -Wextra
int p = 0;
printf("Cnt: %d\n", argc);
while (p < argc) {
printf("Arg %d: %s\n", p, argv[p]);
p += 1;
}
// end only for -Wall -Werror -Wextra
FILE *my_fp;
my_fopen(&my_fp, "./test.txt");
return 0;
}
Valgrind (valgrind -v --track-origins=yes --trace-children=yes --leak-check=full ./test) says:
==960== HEAP SUMMARY:
==960== in use at exit: 568 bytes in 1 blocks
==960== total heap usage: 1 allocs, 0 frees, 568 bytes allocated
1) Why?
2) How to fix it?
you are opening a file but not closing the file,
before returning from main. you can do
if ( 0 != my_fp )
fclose(my_fp)
Well, the problem here is not closing the file pointer before leaving main().
Just add
if (my_fp)
fclose(my_fp);
before return 0; in main().