I'm studying the C language for just 2 weeks now and I'm facing problems with the dynamic allocs and the pointers, I'm a little bit confused about the following code.
I try to understand how the read function work but the *m confuse me. I just can't find a way to use the *m parametre, also the
if((*h = (struct inhabitant *)malloc(sizeof(struct inhabitant) * (*m))) == NULL)
I'm just blown away.
Here's the code:
#include <stdlib.h>
#include "inhabitants.h"
#include "sort.h"
void read(FILE *s, struct inhabitant **h, int *m) {
int i, ntok;
struct inhabitant *tmph;
ntok = fscanf(s, "%d", m);
if(ntok != 1 || *m < 0) {
fprintf(stderr, "Unable to read file.\n");
exit(-1);
}
if((*h = (struct inhabitant *)malloc(sizeof(struct inhabitant) * (*m))) == NULL) {
fprintf(stderr, "Unable to allocate space for inhabitants.\n");
exit(-1);
}
tmph = *h;
for(i = 0; i < (*m); ++i) {
ntok = fscanf(s, "%d %s %s %d", &(tmph[i].distance), (char *)&(tmph[i].prenom), (char *)&(tmph[i].nom), (int *)&(tmph[i].zip));
if(ntok != 4) {
fprintf(stderr, "File wrongly formatted.\n");
exit(-1);
}
}
}
int compare_inhabitants_by_distance(struct inhabitant *a, struct inhabitant *b) {
if (a->distance > b->distance)
return 1;
else
return 0;
//à compléter
}
int compare_inhabitants_by_zipcode(struct inhabitant *a, struct inhabitant *b) {
if (a->enum zipcode > b->enum zipcode)
return 1;
else
return 0;
//à compléter
}
void show(int n, struct inhabitant *a) {
int i;
for(i = 0; i < n; ++i) {
printf("%d, %s, %s, %d\n", a[i].distance, a[i].prenom, a[i].nom, a[i].zip);
}
}
void printout(FILE *s, int n, struct inhabitant *a) {
int i;
for(i = 0; i < n; ++i) {
fprintf(s, "%d %s %s %d\n", a[i].distance, a[i].prenom, a[i].nom, a[i].zip);
}
}
#define PERSONS_TO_SAVE_FILE_IN "evacuation_plan0.txt"
#define PERSONS_TO_SAVE_FILE_OUT "better_evacuation_plan0.txt"
int main(int argc, char **argv) {
FILE *s;
int n;
/*For testing purpose :
struct inhabitant inhabs[] = {
{ 100, "Jean", "France", GUADELOUPE },
{ 10, "Ameni", "Braminia", STBARTH },
{ 12, "Mathieu", "Krister", GUADELOUPE },
{ 23, "Hilaire ", "Blanchi", STMARTIN }
};
n = sizeof(inhabs) / sizeof(*inhabs);*/
struct inhabitant *inhabs0;
if((s = fopen(PERSONS_TO_SAVE_FILE_IN, "r")) == NULL) {
fprintf(stderr, "Unable to open file.");
exit(-1);
}
read(s, inhabs, )
/*
A compléter :
- Lecture du fichier.
- Tris.
*/
if((s = fopen(PERSONS_TO_SAVE_FILE_OUT, "w+")) == NULL) {
fprintf(stderr, "Unable to open file.");
exit(-1);
}
printout(s, n, inhabs0);
fclose(s);
free(inhabs0);
return EXIT_SUCCESS;
}
In C, an assignment is some expression, and expressions are a simple (and very common) kind of statement.
So instead of
int a = 2+3;
if (a>4) {
you might code
int a;
if ((a=2+3) > 4) {
which has the same semantics.
So your if statement is similar to:
*h = (struct inhabitant *)malloc(sizeof(struct inhabitant) * (*m));
if (*h == NULL)
(using a complex if like in the original code, or splitting it into two statements, is a matter of readability and taste; however, be aware of sequence points)
BTW, what follows in the original code is poor taste (and using exit(3) with -1 is also poor taste). You'll better use perror(3) or strerror(3) with errno(3) to explain why malloc failed. So I recommend then
{ // when *h is NULL because malloc failed
fprintf(stderr,
"Unable to allocate space for %d inhabitants: %s\n",
*m, strerror(errno));
exit(EXIT_FAILURE);
}
and in your particular case (allocating an array) I would recommend using calloc(3) (to get some zero-initialized memory zone) instead of malloc.
BTW, I recommend changing the name of your read function. It can be confused with POSIX read.
Don't forget to compile with all warnings and debug info, so gcc -Wall -Wextra -g with GCC (read about Invoking GCC). Improve your code to get no warnings. Read the documentation of every function before using it. Learn to use the debugger gdb. Work hard to avoid undefined behavior, and be very scared of them.
Read also more about C dynamic memory allocation, pointer aliasing, virtual address space, memory leaks, garbage collection, reference counting. In addition of the gdb debugger, valgrind is sometimes useful too. Study also the source code of existing free software (e.g. on github), you'll learn a lot.
In addition to the other error pointed out, your fscanf variables are wrong. You do no need to pass the address of prenum or nom as they are already pointers, e.g.
for (i = 0; i < *m; ++i) {
/* casts should not be necessary, prenom & nom already pointers */
ntok = fscanf (s, "%d %s %s %d", &tmph[i].distance, (tmph[i].prenom),
(tmph[i].nom), &(tmph[i].zip));
Further, the allocation itself is somewhat clumsy. Use the variable name instead of the type for sizeof and there is no need to cast the return of malloc, e.g.
/* allocate/validate space for 'm' inhabitants */
if ((*h = malloc (sizeof **h * *m)) == NULL) {
fprintf (stderr, "Unable to allocate space for inhabitants.\n");
exit (-1);
}
See: Do I cast the result of malloc?
Putting it altogether, your read function could be updated to something similar to:
void read (FILE *s, struct inhabitant **h, int *m)
{
int i, ntok;
struct inhabitant *tmph;
ntok = fscanf (s, "%d", m);
if (ntok != 1 || *m < 0) {
fprintf (stderr, "Unable to read file.\n");
exit (-1);
}
/* allocate/validate space for 'm' inhabitants */
if ((*h = malloc (sizeof **h * *m)) == NULL) {
fprintf (stderr, "Unable to allocate space for inhabitants.\n");
exit (-1);
}
tmph = *h;
for (i = 0; i < *m; ++i) {
/* casts should not be necessary, prenom & nom already pointers */
ntok = fscanf (s, "%d %s %s %d", &tmph[i].distance, (tmph[i].prenom),
(tmph[i].nom), &(tmph[i].zip));
if (ntok != 4) {
fprintf (stderr, "File wrongly formatted.\n");
exit (-1);
}
}
}
note: you also have an apparent typo here in main(), read(s, inhabs, ) appears intended as read (s, &inhabs0); If you give read a meaningful return type, like perhaps int, then you could return 0 or 1 to indicate success/failure without automatically exiting your program from within the function.
Related
I am currently taking a procedural programming course at my school. We are using C with C99 standard. I discussed this with my instructor and I cannot understand why realloc() is working for his machine, but it is not working for mine.
The goal of this program is to parse a text file students.txt that has students' name and their GPA formatted like this:
Mary 4.0
Jack 2.45
John 3.9
Jane 3.8
Mike 3.125
I have a function that resizes my dynamically allocated array, and when I use realloc the debugger in my CLion IDE, it gave me SIGABRT.
I tried using an online compiler and I get realloc(): invalid next size.
I have been trying to debug this all weekend and I can't find the answer and I need help.
My code is currently looking like this
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITIAL_SIZE 4
#define BUFFER_SIZE 512
#define GRADE_CUTOFF 3.9
// ERROR CODES
#define FILE_OPEN_ERROR 1
#define MEMORY_ALLOCATION_ERROR 2
struct student {
double gpa;
char *name;
};
struct student *resizeAllocationIfNeeded(struct student *listOfStudents,
unsigned int studentCount, size_t *currentSize) {
if (studentCount <= *currentSize) {
return listOfStudents;
}
*currentSize *= 2;
struct student *resizedList = (struct student *) realloc(listOfStudents, *currentSize * sizeof(struct student));
if (resizedList == NULL) {
perror("Failed to allocate memory");
exit(MEMORY_ALLOCATION_ERROR);
}
return resizedList;
}
size_t getNamesAndGrades(FILE *file, struct student *listOfStudents, size_t size) {
unsigned int studentCount = 0;
char buffer[BUFFER_SIZE];
while(fscanf(file, "%s %lf", buffer, &listOfStudents[studentCount].gpa) > 0) {
listOfStudents[studentCount].name = strdup(buffer);
studentCount++;
listOfStudents = resizeAllocationIfNeeded(listOfStudents, studentCount, &size);
}
return studentCount;
}
void swapStudents(struct student *listOfStudents, int x, int y) {
struct student temp = listOfStudents[x];
listOfStudents[x] = listOfStudents[y];
listOfStudents[y] = temp;
}
void sortStudentsByGPA(struct student *listOfStudents, unsigned int studentCount) {
for (int i = 0; i < studentCount; i++) {
for (int j = 0; j < studentCount - i - 1; j++) {
if (listOfStudents[j].gpa < listOfStudents[j + 1].gpa) {
swapStudents(listOfStudents, j, j + 1);
}
}
}
}
void printStudentAndGPA(struct student *listOfStudents, unsigned int studentCount) {
for (int i = 0; i < studentCount; i++) {
if (listOfStudents[i].gpa > GRADE_CUTOFF) {
printf("%s %lf\n", listOfStudents[i].name, listOfStudents[i].gpa);
}
free(listOfStudents[i].name);
}
}
void topStudents(char *fileName) {
FILE *file = fopen(fileName, "r");
if (!file) {
perror("Could not open file for reading");
exit(FILE_OPEN_ERROR);
}
struct student *listOfStudents = (struct student *) malloc(INITIAL_SIZE * sizeof(struct student));
if (listOfStudents == NULL) {
perror("Failed to allocate memory");
exit(MEMORY_ALLOCATION_ERROR);
}
unsigned int studentCount = getNamesAndGrades(file, listOfStudents, INITIAL_SIZE);
sortStudentsByGPA(listOfStudents, studentCount);
printStudentAndGPA(listOfStudents, studentCount);
free(listOfStudents);
}
int main() {
topStudents("students.txt");
return 0;
}
You have a fencepost error when checking whether you need to resize the array.
Your initial allocation size is 4, which means that the highest valid index is 3.
In the loop in getNamesAndGrades(), after you read into listOfStudents[3] you increment studentCount to 4. Then you call resizeAllocationIfNeeded(listOfStudents, studentCount, &size);
Inside resizeAllocationIfNeeded(), studentCount == 4 and *currentSize == 4. So the test
if (studentCount <= *currentSize) {
return listOfStudents;
}
succeeds and you return without calling realloc().
Then the next iteration of the loop assigns to listOfStudents[4], which causes a buffer overflow.
You need to change that condition to studentCount < *currentSize.
There are two errors in your code: one is just a typo, the other is a more serious logical error.
First, you are reallocating too late, because of the condition in resizeAllocationIfNeeded(). When studentCount == currentSize, this doesn't resize (even though it should), which makes you overflow the array of students and causes problems.
You can change the condition to fix this:
if (studentCount < *currentSize) {
return listOfStudents;
}
Apart from the above, your main error is in getNamesAndGrades(), where you are reallocating memory and assigning the new pointers to a local variable. You then use that variable in topStudents() as if it was updated. This will of course not work, as the initial pointer passed by topStudents() becomes invalid after the first realloc() and memory is irrevocably lost when getNamesAndGrades() returns.
You should either pass a pointer to the student array, or better just make the function create the array for you.
Here's a solution, renaming getNamesAndGrades to getStudents:
struct student *getStudents(FILE *file, unsigned int *studentCount) {
char buffer[BUFFER_SIZE];
struct student *listOfStudents;
size_t size = INITIAL_SIZE;
*studentCount = 0;
listOfStudents = malloc(size * sizeof(struct student));
if (listOfStudents == NULL) {
perror("Failed to allocate memory");
exit(MEMORY_ALLOCATION_ERROR);
}
while(fscanf(file, "%511s %lf", buffer, &listOfStudents[*studentCount].gpa) == 2) {
listOfStudents[*studentCount].name = strdup(buffer);
(*studentCount)++;
listOfStudents = resizeAllocationIfNeeded(listOfStudents, *studentCount, &size);
}
return listOfStudents;
}
// ...
void topStudents(char *fileName) {
FILE *file = fopen(fileName, "r");
if (!file) {
perror("Could not open file for reading");
exit(FILE_OPEN_ERROR);
}
unsigned int studentCount;
struct student *listOfStudents = getStudents(file, &studentCount);
sortStudentsByGPA(listOfStudents, studentCount);
printStudentAndGPA(listOfStudents, studentCount);
free(listOfStudents);
}
int main() {
topStudents("students.txt");
return 0;
}
Additional notes:
When scanning on a fixed size buffer (in this case 512 bytes), use %511s, not just %s, that's a buffer overflow waiting to happen.
You are scanning two fields, so check if fscanf's return value is == 2, not > 0, you don't want for example one field initialized and one not.
Don't cast the result of malloc() or realloc()
For the future, if you are on Linux, compiling with gcc -g -fsanitize=address will give you detailed error reports when something goes bad in the heap, telling you exactly where memory was allocated, freed and used.
I'm new in c and sorry for my poor English.
I'm trying to write a program that ask to user if he want enter data (region, date of detection, mm of rain) using keyboard and save it in file or if he want give it file's name.
No problem at this time and file is written or read correctly.
File have this structure:
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
....
Try with scanf() (not report main because there is no problem in it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum mese_e {Gen=1, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic} tipo_mese;
typedef struct data_s
{
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s
{
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s
{
tipo_dati_file* array;
int count;
} tipo_ritorna;
int conta_righe(char* Nome_f)
{
int i=0;
char c;
FILE* file;
file=fopen(Nome_f,"r");
while ((c=fgetc(file))!=EOF)
{if(c=='\n')
i++;}
fclose(file);
return i;
}
void crea_array (char* Nome_f)
{
int i,n;
char* regione= (char*)malloc(sizeof(char));
tipo_data data;
int mm_pioggia;
tipo_ritorna risultati;
FILE* file;
n = conta_righe(Nome_f);
printf("%d\n",n);
tipo_dati_file* array = (tipo_dati_file*) malloc (n*sizeof (tipo_dati_file));
file = fopen(Nome_f,"r");
if( file==NULL )
{
printf("Errore in apertura del file!");
exit(1);
}
for(i=0; i<=n; i++)
{
fscanf(file,"%s %d/%d/%d %d\n",regione, &data.giorno, &data.mese, &data.anno, &mm_pioggia);
strcpy(array[i].regione, regione);
array[i].data.giorno=data.giorno;
array[i].data.mese= data.mese;
array[i].data.anno= data.anno;
array[i].mm_pioggia= mm_pioggia;
printf("%s %d/%d/%d %d\n",array[i].regione,array[i].data.giorno, array[i].data.mese,array[i].data.anno,array[i].mm_pioggia);
}
fclose(file);
}
try with fgets()
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef enum mese_e {Gen=1, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic} tipo_mese; typedef struct data_s {
int giorno;
tipo_mese mese;
int anno; } tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia; } tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count; } tipo_ritorna;
int conta_righe(char* Nome_f) {
int i=0;
char c;
FILE* file;
file=fopen(Nome_f,"r");
while ((c=fgetc(file))!=EOF)
{if(c=='\n')
i++;}
fclose(file);
return i;
} void crea_array (char* Nome_f, int v) {
int i=0,s;
char* r;
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file* array = (tipo_dati_file*) malloc (v*sizeof (tipo_dati_file));
file = fopen(Nome_f,"r");
if( file==NULL )
{
printf("Errore in apertura del file!");
exit(1);
}
if (feof(file)==0)
{
char* buf= (char*) malloc(v*sizeof(char));
/*while ( fgets( buf,10000, file) != NULL )
{
r = sscanf( buf, "%s% d/%d/%d %d\n", array[i].regione, &array[i].data.giorno, &array[i].data.mese, &array[i].data.anno, &array[i].mm_pioggia);
printf("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno, array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}*/
while(1)
{
r=fgets( buf,1000, file);
if (r!=NULL)
{
printf("%s",buf);
sscanf( buf, "%s% d/%d/%d %d\n", array[i].regione, &array[i].data.giorno, &array[i].data.mese, &array[i].data.anno, &array[i].mm_pioggia);
printf("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno, array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
else exit(1);
}
}
else exit(1);
fclose(file); }
You have two primary problems I see in crea_array,
You declare char* r; but then attempt to assign the return of sscanf (buf, "%s %d/%d/%d %d\n", ... (e.g. r = sscanf (.... This is incorrect. sscanf returns type int representing the number of successful conversions that took place as specified in your format string (e.g. "%s %d/%d/%d %d\n" would return 5 on success, and remove the '\n', it will cause problems). Your compiler should be screaming warnings at you. If not, you need to enable compiler warnings by adding -Wall -Wextra -pedantic as compiler options and do not accept code until it compiles without a single warning.
crea_array must be declared as type tipo_dati_file * and it must return array; at the end. You must assign the return to a pointer back in the caller. You must also free (buf); before the return or you have just created a memory leak as there is no way to free() the memory you allocated for buf after the function returns. (further, if you are simply allocating 1000-char each time, just use a fixed buffer, e.g. char buf[1000]; and eliminate the need to allocate buf completely.
Putting it altogether, you could do something similar to:
#define MAXC 1000 /* if you need a constant, #define one (or more) */
tipo_dati_file *crea_array (char* Nome_f, int v)
{
int i = 0,
s,
r;
char buf[MAXC] = "";
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file *array = malloc (v * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
exit (EXIT_FAILURE);
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
exit (EXIT_FAILURE);
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", array[i].regione,
&array[i].data.giorno, &array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fput ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
printf ("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno,
array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
fclose (file);
return array;
}
Note: I have not compiled the code above.
Then in main, you could do something similar to:
tipo_dati_file *array = crea_array (name, v);
(note: you should also pass a 3rd parameter of type int *numelem so you can assign *numelem = i; before return making the number of elements filled available back in the caller if less than v are actually read)
If you will post a A Minimal, Complete, and Verifiable Example (MCVE), along with a sample data file (10 lines or so), I'm happy to help further and I can validate the code works -- as I will have something I can compile and run.
Edit Following Warnings Posted (in comments)
The two warnings are addressed in detail in the comments below the answer. Once those are resolved, you will run into a horrible SegFault as you are not allocating storage for array[i].regione.
In you nested set of structs:
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
regione is an uninitialized pointer that points to some indeterminate memory location (which you do not own). When you attempt to write characters there with sscanf (BOOM - SegFault -- most likely).
You have two choices (1) declare regione as a fixed array, e.g. char regione[CONST] (inefficient), or (2) read the string for regione into a temporary buffer and then allocate storage for strlen + 1 chars and copy the string from the temporary buffer to the new block of memory and assign the starting address for that block to regione (you can use strlen/malloc/memcpy or strdup -- if you have it, it does all three)
With that fix and a few tweaks to your crea_array function (like passing a pointer to int to hold the number of struct filled in array instead of v), you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXDATA 64
typedef enum mese_e { Genv= 1, Feb, Mar, Apr, Mag, Giu,
Lug, Ago, Set, Ott, Nov, Dic
} tipo_mese;
typedef struct data_s {
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count;
} tipo_ritorna;
tipo_dati_file *crea_array (char *Nome_f, int *nelem)
{
int i = 0,
r;
char region[MAXC] = ""; /* temp buffer to hold array[i].regione */
char buf[MAXC] = "";
FILE* file;
tipo_dati_file *array = malloc (MAXDATA * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
return NULL;
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
return NULL;
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", region,
&array[i].data.giorno, (int*)&array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fputs ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
array[i].regione = strdup (region);
if (!array[i].regione) { /* strdup allocates - you must validate */
perror ("strdup-array[i].regione");
for (int j = 0; j < i; j++) /* on failure free prior mem */
free (array[j].regione); /* and return NULL */
free (array);
return NULL;
}
i++;
}
fclose (file);
*nelem = i; /* update nelem with number of struct filled */
return array;
}
int main (int argc, char **argv) {
int index = 0,
nelem = 0;
char *datafile = argc > 1 ? argv[1] : "dat/staterain.txt";
tipo_ritorna statistics[MAXDATA] = {{ .array = NULL }};
statistics[index].array = crea_array (datafile, &nelem);
if (statistics[index].array && nelem > 0) {
statistics[index].count = nelem;
for (int i = 0; i < statistics[index].count; i++) {
printf ("%-12s %02d/%02d/%4d %3d\n",
statistics[index].array[i].regione,
statistics[index].array[i].data.giorno,
statistics[index].array[i].data.mese,
statistics[index].array[i].data.anno,
statistics[index].array[i].mm_pioggia);
free (statistics[index].array[i].regione); /* free strings */
}
free (statistics[index].array); /* free array */
}
return 0;
}
Example Use/Output
$ ./bin/staterain
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/staterain
==3349== Memcheck, a memory error detector
==3349== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3349== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3349== Command: ./bin/staterain
==3349==
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
==3349==
==3349== HEAP SUMMARY:
==3349== in use at exit: 0 bytes in 0 blocks
==3349== total heap usage: 5 allocs, 5 frees, 2,110 bytes allocated
==3349==
==3349== All heap blocks were freed -- no leaks are possible
==3349==
==3349== For counts of detected and suppressed errors, rerun with: -v
==3349== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I have this function. As you can see, everything is being done in the function, I'm not allocating in the main and then passing anything to it (I'll only return the pointer to the array once the function is done). The function in itself (with a fixed size for the array) works, but the realloc fails.
struct database *parse() {
int i = 0;
int n = 1;
FILE *dbase = (fopen(PATH, "r"));
if (dbase == NULL) {
fprintf(stderr, ERRORE_APERTURA);
exit(EXIT_FAILURE);
}
struct database *database_array = calloc(20*n, sizeof(struct database));
if (database_array == NULL) {
fprintf(stderr, "Impossibile allocare memoria\n");
exit(EXIT_FAILURE);
}
while (feof(dbase) == 0) {
fscanf(dbase, "%[^:]:%[^:]:\n", database_array[i].user, database_array[i].password);
database_array[i].iswritten = 1;
i++;
if (i > 20*n) {
n++;
struct database *new_database_array = realloc(database_array, sizeof(struct database)*(20*n));
database_array = new_database_array;
}
}
database_array[++i].iswritten = 0;
fclose(dbase);
return database_array;
}
I tried reading other explanations, but I can't understand what's wrong here.
The array I allocated with calloc is initially 20. then, when it's filled, I want it to double in size, so I use n, which will be 2, by 20, so 40.
The frustrating thing is that I tried reallocating an array of struct with a simpler program, and doing THE SAME THING works without any problem:
#include <stdio.h>
#include <stdlib.h>
struct prova {
int a;
int b[10];
};
int main() {
struct prova* array_struct = calloc(10, sizeof(struct prova));
array_struct[0].a = 2;
struct prova* tmp = realloc(array_struct, sizeof(struct prova) * 20);
free(array_struct);
array_struct = tmp;
array_struct[1].b[1] = 3;
printf("a = %d", array_struct[0].a);
printf("b = %d\n", array_struct[1].b[1]);
return 0;
}
What am I not seeing? (Please nevermind the fact that I'm not checking if realloc returns NULL, I'll add that later)
struct database *new_database_array = realloc(database_array, sizeof(struct database)*(20*n));
free(database_array);
You can't both reallocate something and deallocate it. You can do either, but once you've done either, the previous allocation no longer exists, so you can't do the other.
After the first line of code above, the value of database_array should not be used anymore because it may not be valid.
I apologize in advance, but this is kind of a long one.
In my program, I read in the student's information, but when I go to output it, it comes out scrambled, and then gets a pointer error.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student{
char *firstName;
char *lastName;
char id[10];
char gender;
int age;
double gpa;
};
void main()
{
int n;
struct student *classroom;
printf("How many students?");
scanf("%d",&n);
classroom = (struct student*) malloc(n*sizeof(struct student));
if (classroom == NULL)
exit(1);
readStudentsInformation(classroom,n);
outputStudents(classroom,n);
printf("The average age is %.2f.\n",averageAge(classroom,n));
printf("The average GPA is %.2f.\n",averageGpa(classroom,n));
sortByLastName(classroom,n);
outputStudents(classroom,n);
sortByID(classroom,n);
outputStudents(classroom,n);
sortByAge(classroom,n);
}
void outputStudents(struct student classroom[], int size)
{
int i;
for (i = 0; i < size; i++)
{
printf("%15s",classroom[i].firstName);
printf("%15s:",classroom[i].lastName);
printf("%14s,",classroom[i].id);
printf("%3c",classroom[i].gender);
printf("%5d",classroom[i].age);
printf("%5.2f",classroom[i].gpa);
}
}
Input:
How many students?2
First Name?Thom
Last Name?Arron
ID?2
Gender?M
Age?26
GPA?3.9
First Name?Frank
Last Name?Roberts
ID?1
Gender?F
Age?24
GPA?3.4'
Output:
Roberts Roberts: 2, M 26 3.90 : 1, F 24 3.40The average age is 25.00.
The average GPA is 3.65.
* glibc detected * ./lab12: munmap_chunk(): invalid pointer: 0x00007fff30319a90 *
: 2, M 26 3.90Aborted (core dumped)
The full code is here, but I didn't want to copy 200 lines to stack overflow: http://codepad.org/LYpS6t5z
Any idea what would cause this?
This is one conceptual error, you have it in a couple of places
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
classroom[i].firstName = temp;
What you want here instead is
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
strcpy(classroom[i].firstName, temp); // note this
Or, cleaned up a bit:
classroom[i].firstName = malloc(1+strlen(temp)); // note clean up here
if (classroom[i].firstName == NULL)
exit(1);
strcpy(classroom[i].firstName, temp);
Or even just
classroom[i].firstName = strdup(temp); // this takes place of all the lines above
These errors explain why your free's are failing.
Nothing else jumps out at me.
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
classroom[i].firstName = temp;
Your second assignment here overwrites the address, leaking the mallocd memory and making the pointer invalid as soon as that for iteration finishes. The same buffer is reused (with the same error) for lastName, which is why you see Roberts Roberts instead of the actual first and last name. When you go to free them, they are (1) invalid and (2) not made by malloc, so you get the crash you see.
Just like other arrays, you can't copy them by assignment, you have to copy byte-by-byte:
size_t len = strlen(temp);
classroom[i].firstName = malloc(1+len);
if (classroom[i].firstName == NULL)
exit(1);
strncpy(classroom[i].firstname, temp, len);
classroom[i].firstname[len] = '\0';
And don't cast the result of malloc.
Weirdness happening in readStudentsInformation, in particular lines like
classroom[i].lastName = temp;
are causing issues later on when you try to free this memory with
free(classroom[i].firstName);
Appropriate memory handling below:
void readStudentsInformation(struct student classroom[], int size)
{
int i;
char temp[50];
for (i = 0; i < size; i++)
{
printf("First Name?");
scanf("%s",temp);
classroom[i].firstName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].firstName == NULL)
exit(1);
/* after mallocing good memory can write in the data.. */
strcpy(classroom[i].firstName, temp);
/* classroom[i].firstName = temp; */
printf("Last Name?");
scanf("%s",temp);
classroom[i].lastName = (char*)malloc(sizeof(char)*(1+strlen(temp)));
if (classroom[i].lastName == NULL)
exit(1);
/* classroom[i].lastName = temp; */
strcpy(classroom[i].lastName, temp);
printf("ID?");
scanf("%s",classroom[i].id);
fflush(stdin);
__fpurge(stdin);
printf("Gender?");
scanf("%c",&classroom[i].gender);
printf("Age?");
scanf("%d",&classroom[i].age);
printf("GPA?");
scanf("%lf",&classroom[i].gpa);
}
}
The following function gets file offsets from the rabin_polynomial structure, opens the input_file for md5 fingerprint generation and writes the result to fpfile
My problem is it seems to use the same chunk_buffer content some times that it generates similar fingerprints for chunks with different legths.
What could be the reason?
I have tested the md5 function with other inputs separately and it generates correct digests.
int write_rabin_fingerprints_to_binary_file(FILE *fpfile,FILE *input_file,
struct rabin_polynomial *head)
{
struct rabin_polynomial *poly=head;
unsigned char fing_print[33]={'\0'};
size_t bytes_read;
while(poly != NULL)
{
char *chunk_buffer;
chunk_buffer = (char*) malloc ((poly->length));
bytes_read=fread (chunk_buffer,1, poly->length,input_file);
if(bytes_read!=poly->length)
{
printf("Error reading from%s ",input_file);
return -1;
}
strncpy((char*)fing_print,md5(chunk_buffer).c_str(),32);
size_t ret_val=fprintf(fpfile, "%llu\t%lu\t%s\n",poly->start,
poly->length,fing_print);
if(ret_val == 0)
{
fprintf(stderr, "Could not write rabin polynomials to file.");
return -1;
}
poly=poly->next_polynomial;
free(chunk_buffer);
}
return 0;
}
EDIT:
I am running this program using visual studio 2010. Could typecasting to char * in the malloc() line create the problem?
The number of bytes read is just as specified in the argument.
There was nothing wrong in the code to cause such faults. I just found out that it happened because of zero-length strings which are also called as file holes.
int write_rabin_fingerprints_to_binary_file(FILE *fpfile,FILE *input_file
, struct rabin_polynomial *head)
{
struct rabin_polynomial *poly;
unsigned char fing_print[33];
for (poly=head; poly != NULL;poly=poly->next_polynomial ) {
char *chunk_buffer;
int retval;
chunk_buffer = malloc (1+poly->length);
retval = fread (chunk_buf,1, poly->length,input_file);
/* check retval here */
chunk_buff[poly->length] = 0;
strncpy(fing_print,md5(chunk_buffer).c_str(), sizeof fing_print);
fing_print[sizeof fing_print -1] = 0;
retval = fprintf(fpfile, "%llu\t%lu\t%s\n"
,poly->start, poly->length, fing_print);
if(retval <= 0)
{
fprintf(stderr, "Could not write rabin polynomials to file.");
return -1;
}
free(chunk_buffer);
}
return 0;
}