I have a program that only does two things for now. Read data from two files, into two arrays as structs, and then freeing these arrays. These stucts have strings in them, so I malloc()
memory for them manually. Everything was working, I could print everything correctly, so I wrote the free()
ing methods for these arrays, which were working as well. I also prepared the program to be able to read more data, if the files were bigger, and that's where the problems came. I keep track of the capacity and the actual size, and if size is at capacity, I simply realloc()
the array and continue reading. I check if the realloc()
was successful, and if not, the program exits. I tried to test it, but it seems that I have done something wrong and I cannot figure out what happens.
If I need to realloc()
valgrind
reports errors, leaks, and eventually the program exits with Segmentation fault
. It seems if I have enough initial capacity there aren't any problems, no leaks, no errors, no Segmentation fault
.
As problems only emerge after realloc()
I naturally tried to research the use of realloc()
, but I did not find what can go wrong. I cannot see why this could happen.
Here is the code:
menu.c
#define BUFSIZE 1024
#define INITCAP 8
void runMenu()
{
int addressesSize = 0;
int applicationsSize = 0;
Address *addresses = malloc(INITCAP * sizeof(Address));
Application *applications = malloc(INITCAP * sizeof(Application));
initializeAddresses(addresses, &addressesSize);
initializeData(applications, &applicationsSize);
freeAddresses(addresses, addressesSize);
freeApplications(applications, applicationsSize);
}
void initializeAddresses(Address *addresses, int *size)
{
int actCap = INITCAP;
char buffer[BUFSIZE];
FILE *input = fopen("addresses.txt", "r");
if (input == NULL)
{
IOError("addresses.txt");
}
if (addresses == NULL)
{
nomem();
}
while (fgets(buffer, BUFSIZE, input))
{
if (*size == actCap)
{
actCap *= 2;
addresses = realloc(addresses, actCap * sizeof(*addresses));
if (addresses == NULL)
{
nomem();
}
}
int len = strlen(buffer);
if (isspace(buffer[len - 1]))
{
buffer[len - 1] = '\0';
}
Address a;
a.name = malloc((len + 1) * sizeof(char));
if (a.name == NULL)
{
nomem();
}
a.id = *size;
strcpy(a.name, buffer);
addresses[(*size)++] = a;
}
fclose(input);
}
void initializeData(Application *applications, int *size)
{
int actCap = INITCAP;
char buffer[BUFSIZE];
FILE *input = fopen("data.txt", "r");
if (input == NULL)
{
IOError("data.txt");
}
if (applications == NULL)
{
nomem();
}
printf("starting read from data.txt\n");
while (fgets(buffer, BUFSIZE, input))
{
printf("size is %d\n", *size);
if (*size == actCap)
{
printf("size is at capacity: %d\n", actCap);
actCap *= 2;
printf("capacity now: %d\nreallocing\n", actCap);
applications = realloc(applications, actCap * sizeof(*applications));
if (applications == NULL)
{
nomem();
}
printf("realloced successfully\n");
}
Application a;
char *nameBuf = strtok(buffer, ";");
a.name = malloc((strlen(nameBuf) + 1) * sizeof(char));
strcpy(a.name, nameBuf);
char *addressBuf = strtok(0, ";");
a.address = malloc((strlen(addressBuf) + 1) * sizeof(char));
strcpy(a.address, addressBuf);
char *strNumOfApplicationBuf = strtok(0, ";");
int numOfApplication = atoi(strNumOfApplicationBuf);
if (numOfApplication <= 0)
{
formatError("Az egyik jelentkezőhöz a jelentkezésének száma helytelenül van megadva");
}
a.numOfApplication = numOfApplication;
if ((strtok(0, ";")) != NULL)
{
formatError("A jelentkezéseket tartalmazó fájl több oszlopot tartlamaz a megengedettnél");
}
a.id = *size;
applications[(*size)++] = a;
}
fclose(input);
}
void freeAddresses(Address *list, int size)
{
for (int i = 0; i < size; ++i)
{
free(list[i].name);
}
free(list);
}
void freeApplications(Application *list, int size)
{
printf("\nthere are %d applications\n", size);
for (int i = 0; i < size; i++)
{
printf("freeing: %d: name = %s\n", i, list[i].name);
free(list[i].name);
free(list[i].address);
}
free(list);
}
void nomem()
{
fprintf(stderr, "Nincs memória\n");
exit(1);
}
void IOError(char *fileName)
{
fprintf(stderr, "Nem sikerült megnyitni egy szükséges fájlt: %s\n", fileName);
exit(1);
}
void formatError(char *what)
{
fprintf(stderr, "Formátum hiba: %s\n", what);
exit(1);
}
Address
typedef struct Address
{
char* name;
int id;
} Address;
Application
typedef struct Application
{
char* name;
char* address;
int numOfApplication;
int id;
} Application;
I have a main.c
which for now only calls runMenu()
. If I increase INITCAP
to say 80, which is more than needed, this is the output from valgrind
reports 0 errors and no leaks possible.
If I reduce it back to 8 this is the output:
==44== Memcheck, a memory error detector
==44== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==44== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==44== Command: ./a.out
==44==
starting read from data.txt
size is 0
size is 1
size is 2
size is 3
size is 4
size is 5
size is 6
size is 7
size is 8
size is at capacity: 8
capacity now: 16
reallocing
realloced successfully
size is 9
size is 10
size is 11
size is 12
there are 13 applications
==44== Invalid read of size 8
==44== at 0x1097C0: freeApplications (in a.out)
==44== by 0x1092B3: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44== Address 0x4a16100 is 0 bytes inside a block of size 192 free'd
==44== at 0x483AD7B: realloc (vg_replace_malloc.c:834)
==44== by 0x109563: initializeData (in a.out)
==44== by 0x109291: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44== Block was alloc'd at
==44== at 0x483877F: malloc (vg_replace_malloc.c:307)
==44== by 0x109267: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44==
freeing: 0: name = Tapsi Hapsi
==44== Invalid read of size 8
==44== at 0x1097F6: freeApplications (in a.out)
==44== by 0x1092B3: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44== Address 0x4a16100 is 0 bytes inside a block of size 192 free'd
==44== at 0x483AD7B: realloc (vg_replace_malloc.c:834)
==44== by 0x109563: initializeData (in a.out)
==44== by 0x109291: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44== Block was alloc'd at
==44== at 0x483877F: malloc (vg_replace_malloc.c:307)
==44== by 0x109267: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44==
==44== Invalid read of size 8
==44== at 0x10981E: freeApplications (in a.out)
==44== by 0x1092B3: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44== Address 0x4a16108 is 8 bytes inside a block of size 192 free'd
==44== at 0x483AD7B: realloc (vg_replace_malloc.c:834)
==44== by 0x109563: initializeData (in a.out)
==44== by 0x109291: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44== Block was alloc'd at
==44== at 0x483877F: malloc (vg_replace_malloc.c:307)
==44== by 0x109267: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44==
freeing: 1: name = Nyúl Péter
freeing: 2: name = Nyuszi Dani
freeing: 3: name = Nyuszó Lóránt
freeing: 4: name = Nyuszika Marci
freeing: 5: name = Nyúl Endre
freeing: 6: name = Tapsi Márton
freeing: 7: name = Tapsi Csongor
freeing: 8: name = (null)
==44== Invalid read of size 1
==44== at 0x483BC82: strlen (vg_replace_strmem.c:459)
==44== by 0x48B9F75: __vfprintf_internal (vfprintf-internal.c:1688)
==44== by 0x48A4D9A: printf (printf.c:33)
==44== by 0x1097D8: freeApplications (in a.out)
==44== by 0x1092B3: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44== Address 0x100 is not stack'd, malloc'd or (recently) free'd
==44==
==44==
==44== Process terminating with default action of signal 11 (SIGSEGV)
==44== Access not within mapped region at address 0x100
==44== at 0x483BC82: strlen (vg_replace_strmem.c:459)
==44== by 0x48B9F75: __vfprintf_internal (vfprintf-internal.c:1688)
==44== by 0x48A4D9A: printf (printf.c:33)
==44== by 0x1097D8: freeApplications (in a.out)
==44== by 0x1092B3: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44== If you believe this happened as a result of a stack
==44== overflow in your program's main thread (unlikely but
==44== possible), you can try to increase the size of the
==44== main thread stack using the --main-stacksize= flag.
==44== The main thread stack size used in this run was 8388608.
freeing: 9: name = ==44==
==44== HEAP SUMMARY:
==44== in use at exit: 508 bytes in 11 blocks
==44== total heap usage: 41 allocs, 30 frees, 4,089 bytes allocated
==44==
==44== 508 (384 direct, 124 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
==44== at 0x483AD7B: realloc (vg_replace_malloc.c:834)
==44== by 0x109563: initializeData (in a.out)
==44== by 0x109291: runMenu (in a.out)
==44== by 0x109232: main (in a.out)
==44==
==44== LEAK SUMMARY:
==44== definitely lost: 384 bytes in 1 blocks
==44== indirectly lost: 124 bytes in 10 blocks
==44== possibly lost: 0 bytes in 0 blocks
==44== still reachable: 0 bytes in 0 blocks
==44== suppressed: 0 bytes in 0 blocks
==44==
==44== For lists of detected and suppressed errors, rerun with: -s
==44== ERROR SUMMARY: 30 errors from 5 contexts (suppressed: 0 from 0)
Segmentation fault
note: I replaced the real path to just a.out