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.
Related
I want to implement a searching table and
here's the data:
20130610 Diamond CoinMate 11.7246 15.7762 2897
20130412 Diamond Bithumb 0.209 0.2293 6128
20130610 OKCash Bithumb 0.183 0.2345 2096
20130412 Ethereum Chbtc 331.7282 401.486 136786
20170610 OKCash Tidex 0.0459 0.0519 66
...
and my code
typedef struct data{
int *date;
string currency[100];
string exchange[100];
double *low;
double *high;
int *daily_cap;
} Data;
int main()
{
FILE *fp = fopen("test_data.txt", "r");
Data tmp[50];
int i = 0;
while (!feof(fp)){
fscanf(fp, "%d%s%s%f%f%7d", &tmp[i].date, tmp[i].currency, tmp[i].exchange, &tmp[i].low, &tmp[i].high, &tmp[i].daily_cap);
i++;
}
fclose(fp);
}
but the first problem is that I can't create a large array to store my struct like
Data tmp[1000000]
and even I try just 50 elements , the program break down when finish main().
can anyone tell how to fix it or give me a better method, thanks.
You can not scan a value to an unallocated space, in other words, you need room for all those pointers in the struct, switch to
typedef struct data{
int date;
string currency[100];
string exchange[100];
double low;
double high;
int daily_cap;
} Data;
Or use malloc to assign space to those pointers before using them.
while (!feof(fp)){
tmp[i].date = malloc(sizeof(int));
...
But in this case, you don't need to pass the address of such members to fscanf since they are already pointers:
fscanf(fp, "%d%s%s%f%f%7d", &tmp[i].date, ..
should be
fscanf(fp, "%d%s%s%lf%lf%7d", tmp[i].date, ...
Notice that double wants %lf instead of %f
This is also very confusing:
typedef struct data{
int *date;
string currency[100];
...
Is string a typedef of char? I think you mean string currency; since string is usually an alias of char *, in this case you need room for this member too: currency = malloc(100);
Finally, take a look to Why is “while ( !feof (file) )” always wrong?
There are too many errors in a short snippet, I suggest you to read a good C book.
Your code corrected using dynamic memory that allows you to reserve space for a big amount of data (see the other answer of #LuisColorado) and using fgets and sscanf instead of fscanf:
#include <stdio.h>
#include <stdlib.h>
typedef struct data{
int date;
char currency[100];
char exchange[100];
double low;
double high;
int daily_cap;
} Data;
int main(void)
{
FILE *fp = fopen("test_data.txt", "r");
/* Always check the result of fopen */
if (fp == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
Data *tmp;
tmp = malloc(sizeof(*tmp) * 50);
if (tmp == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
char buf[512];
int i = 0;
/* Check that you don't read more than 50 lines */
while ((i < 50) && (fgets(buf, sizeof buf, fp))) {
sscanf(buf, "%d%99s%99s%lf%lf%7d", &tmp[i].date, tmp[i].currency, tmp[i].exchange, &tmp[i].low, &tmp[i].high, &tmp[i].daily_cap);
i++;
}
fclose(fp);
/* Always clean what you use */
free(tmp);
return 0;
}
Of course you can't. Think you are creating an array of 1.0E6 registers of sizeof (Data) which I guess is not less than 32 (four pointers) and 200 bytes (not less than this, as you don't give the definition of type string) and this is 232MBytes (at least) in a 64 byte machine (in 32bit it is 216MBytes) and that in case the type string is only one character wide (what I fear is not) In case string is a typedef of char * then you have 432 pointers in your struct giving to 432MBytes in only one variable. Next, if you are declaring this absolutely huge variable as a local variable, you must know that te stack in most unix operating systems is limited to around 8Mb, and this means you need to build your program with special parameters to allow a larger stack max size. And also you probably need your account to raise to that size also the ulimits to make the kernel to allow you such a large stack size segment.
Please, next time, give us full information, as not knowing the definition of the string type, or posting an incomplete program, only allows us to make guesses on what can be ongoing, and not to be able to discover actual errors. This makes you to waste your time, and for us the same. Thanks.
If your list of currency and exchange are known before hand, then there is no need to allocate or store any arrays within your struct. The lists can be global arrays of pointers to string literals and all you need do is store a pointer to the literal for both currency and exchange (you can even save a few more bytes by storing the index instead of a pointer).
For example, your lists of exchanges can be stored once as follows:
const char *currency[] = { "Diamond", "OKCash", "Ethereum" },
*exchange[] = { "CoinMate", "Bithumb", "Chbtc", "Tidex" };
(if the number warrants, allocate storage for the strings and read them from a file)
Now you have all of the possible strings for currency and exchange stored, all you need in your data struct is a pointer for each, e.g.
typedef struct {
const char *currency, *exchange;
double low, high;
unsigned date, daily_cap;
} data_t;
(unsigned gives a better range and there are no negative dates or daily_cap)
Now simply declare an array of data_t (or allocate for them, depending on number). Below is a simply array of automatic storage for example purposes. E.g.
#define MAXD 128
...
data_t data[MAXD] = {{ .currency = NULL }};
Since you are reading 'lines' of data, fgets or POSIX getline are the line-oriented choices. After reading a line, you can parse the line with sscanf using temporary values, compare whether the values for currency and exchange read from the file match values stored, and then assign a pointer to the appropriate string to your struct, e.g.
int main (void) {
char buf[MAXC] = "";
size_t n = 0;
data_t data[MAXD] = {{ .currency = NULL }};
while (n < MAXD && fgets (buf, MAXC, stdin)) {
char curr[MAXE] = "", exch[MAXE] = "";
int havecurr = 0, haveexch = 0;
data_t tmp = { .currency = NULL };
if (sscanf (buf, "%u %31s %31s %lf %lf %u", &tmp.date,
curr, exch, &tmp.low, &tmp.high, &tmp.daily_cap) == 6) {
for (int i = 0; i < NELEM(currency); i++) {
if (strcmp (currency[i], curr) == 0) {
tmp.currency = currency[i];
havecurr = 1;
break;
}
}
for (int i = 0; i < NELEM(exchange); i++) {
if (strcmp (exchange[i], exch) == 0) {
tmp.exchange = exchange[i];
haveexch = 1;
break;
}
}
if (havecurr & haveexch)
data[n++] = tmp;
}
}
...
Putting it altogether in a short example, you could do something similar to the following:
#include <stdio.h>
#include <string.h>
#define MAXC 256
#define MAXD 128
#define MAXE 32
#define NELEM(x) (int)(sizeof (x)/sizeof (*x))
const char *currency[] = { "Diamond", "OKCash", "Ethereum" },
*exchange[] = { "CoinMate", "Bithumb", "Chbtc", "Tidex" };
typedef struct {
const char *currency, *exchange;
double low, high;
unsigned date, daily_cap;
} data_t;
int main (void) {
char buf[MAXC] = "";
size_t n = 0;
data_t data[MAXD] = {{ .currency = NULL }};
while (n < MAXD && fgets (buf, MAXC, stdin)) {
char curr[MAXE] = "", exch[MAXE] = "";
int havecurr = 0, haveexch = 0;
data_t tmp = { .currency = NULL };
if (sscanf (buf, "%u %31s %31s %lf %lf %u", &tmp.date,
curr, exch, &tmp.low, &tmp.high, &tmp.daily_cap) == 6) {
for (int i = 0; i < NELEM(currency); i++) {
if (strcmp (currency[i], curr) == 0) {
tmp.currency = currency[i];
havecurr = 1;
break;
}
}
for (int i = 0; i < NELEM(exchange); i++) {
if (strcmp (exchange[i], exch) == 0) {
tmp.exchange = exchange[i];
haveexch = 1;
break;
}
}
if (havecurr & haveexch)
data[n++] = tmp;
}
}
for (size_t i = 0; i < n; i++)
printf ("%u %-10s %-10s %8.4f %8.4f %6u\n", data[i].date,
data[i].currency, data[i].exchange, data[i].low,
data[i].high, data[i].daily_cap);
}
Example Use/Output
$ ./bin/coinread <dat/coin.txt
20130610 Diamond CoinMate 11.7246 15.7762 2897
20130412 Diamond Bithumb 0.2090 0.2293 6128
20130610 OKCash Bithumb 0.1830 0.2345 2096
20130412 Ethereum Chbtc 331.7282 401.4860 136786
20170610 OKCash Tidex 0.0459 0.0519 66
With this approach, regardless whether you allocate for your array of struct or use automatic storage, you minimize the size of the data stored by not duplicating storage of known values. On x86_64, your data_t struct size will be approximately 40-bytes. With on average a 1-4 Megabyte stack, you can store a lot of 40-byte structs safely before you need to start allocating. You can always start with automatic storage, and if you reach some percentage of the available stack space, dynamically allocate, memcpy, set a flag to indicate the storage in use and keep going...
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.
I'm trying to make a file system in C. I have trouble with this portion of my code when I'm printing my values in the code below:
for (int i = 0; i<NUM_POINTERS; i++) {
printf("before SB->root[%d]=%d\n", i, SB->root->pointers[i]);
}
write_blocks(0, 1, SB);
for (int i = 0; i<NUM_POINTERS; i++) {
printf("after SB->root[%d]=%d\n", i, SB->root->pointers[i]);
}
my write_blocks method:
int write_blocks(int start_address, int nblocks, void *buffer)
{
int i, e, s;
e = 0;
s = 0;
void* blockWrite = (void*) malloc(BLOCK_SIZE);
/*Checks that the data requested is within the range of addresses of the disk*/
if (start_address + nblocks > MAX_BLOCK)
{
printf("out of bound error\n");
return -1;
}
/*Goto where the data is to be written on the disk*/
fseek(fp, start_address * BLOCK_SIZE, SEEK_SET);
/*For every block requested*/
for (i = 0; i < nblocks; ++i)
{
/*Pause until the latency duration is elapsed*/
usleep(L);
memcpy(blockWrite, buffer+(i*BLOCK_SIZE), BLOCK_SIZE);
fwrite(blockWrite, BLOCK_SIZE, 1, fp);
fflush(fp);
s++;
}
free(blockWrite);
/*If no failure return the number of blocks written, else return the negative number of failures*/
if (e == 0)
return s;
else
return e;
}
And here's what gets printed:
before SB->root[0]=1
before SB->root[1]=2
before SB->root[2]=3
before SB->root[3]=4
before SB->root[4]=5
before SB->root[5]=6
before SB->root[6]=7
before SB->root[7]=8
before SB->root[8]=9
before SB->root[9]=10
before SB->root[10]=11
before SB->root[11]=12
before SB->root[12]=13
before SB->root[13]=14
after SB->root[0]=1234344888
after SB->root[1]=32688
after SB->root[2]=3
after SB->root[3]=4
after SB->root[4]=5
after SB->root[5]=6
after SB->root[6]=7
after SB->root[7]=8
after SB->root[8]=9
after SB->root[9]=10
after SB->root[10]=11
after SB->root[11]=12
after SB->root[12]=13
after SB->root[13]=14
I don't understand why my first and second pointer value change?
Some additional information: SB is a superBlock here's my structures:
typedef struct iNode
{
int id;
int size;
int pointers[NUM_POINTERS];
} iNode;
typedef struct superBlock
{
int magic_number;
int block_size;
int num_blocks;
int num_inodes;
iNode *root;
iNode jNodes[20];
} superBlock;
Is this single threaded?
Does the modified SB->root[0,1] contain the data you are trying to write?
What is your BLOCK_SIZE?
I suspect the problem is outside of write_blocks(). My best guess would be that you accidentally freed SB somewhere and malloc gave you the same address. After the malloc check (print or debugger) both buffer and blockWrite and make sure they are different and valid.
Unrelated Issues:
printf has more % than params
You should check the return of malloc
e is never set
s and i are equal. AKA redundant.
Out of bounds error causes a memory leak (since it is after the malloc)
usleep is strange perhaps you want fsync?
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);
}
}
I am trying to write a Huffman encoding program to compress a text file. Upon completetion, the program will terminate at the return statement, or when I attempt to close a file I was reading from. I assume I have memory leaks, but I cannot find them. If you can spot them, let me know (and a method for fixing them would be appreciated!).
(note: small1.txt is any standard text file)
Here is the main program
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define ASCII 255
struct link {
int freq;
char ch[ASCII];
struct link* right;
struct link* left;
};
typedef struct link node;
typedef char * string;
FILE * ofp;
FILE * ifp;
int writebit(unsigned char);
void sort(node *[], int);
node* create(char[], int);
void sright(node *[], int);
void Assign_Code(node*, int[], int, string *);
void Delete_Tree(node *);
int main(int argc, char *argv[]) {
//Hard-coded variables
//Counters
int a, b, c = 0;
//Arrays
char *key = (char*) malloc(ASCII * sizeof(char*));
int *value = (int*) malloc(ASCII * sizeof(int*));
//File pointers
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "can't open %s\n", argv[1]);
return 0;
}
//Nodes
node* ptr;//, *head;
node* array[ASCII];
//
int u, carray[ASCII];
char str[ASCII];
//Variables
char car = 0;
int inList = 0;
int placeinList = -1;
int numofKeys;
if (argc < 2) {
printf("Usage: huff <.txt file> \n");
return 0;
}
for (a = 0; a < ASCII; a++) {
key[a] = -1;
value[a] = 0;
}
car = fgetc(fp);
while (!feof(fp)) {
for (a = 0; a < ASCII; a++) {
if (key[a] == car) {
inList = 1;
placeinList = a;
}
}
if (inList) {
//increment value array
value[placeinList]++;
inList = 0;
} else {
for (b = 0; b < ASCII; b++) {
if (key[b] == -1) {
key[b] = car;
break;
}
}
}
car = fgetc(fp);
}
fclose(fp);
c = 0;
for (a = 0; a < ASCII; a++) {
if (key[a] != -1) {
array[c] = create(&key[a], value[a]);
numofKeys = c;
c++;
}
}
string code_string[numofKeys];
while (numofKeys > 1) {
sort(array, numofKeys);
u = array[0]->freq + array[1]->freq;
strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);
ptr = create(str, u);
ptr->right = array[1];
ptr->left = array[0];
array[0] = ptr;
sright(array, numofKeys);
numofKeys--;
}
Assign_Code(array[0], carray, 0, code_string);
ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");
car = fgetc(ifp);
while (!feof(ifp)) {
for (a = 0; a < ASCII; a++) {
if (key[a] == car) {
for (b = 0; b < strlen(code_string[a]); b++) {
if (code_string[a][b] == 48) {
writebit(0);
} else if (code_string[a][b] == 49) {
writebit(1);
}
}
}
}
car = fgetc(ifp);
}
writebit(255);
fclose(ofp);
ifp = fopen("small1.txt", "r");
fclose(ifp);
free(key);
//free(value);
//free(code_string);
printf("here1\n");
return 0;
}
int writebit(unsigned char bitval) {
static unsigned char bitstogo = 8;
static unsigned char x = 0;
if ((bitval == 0) || (bitval == 1)) {
if (bitstogo == 0) {
fputc(x, ofp);
x = 0;
bitstogo = 8;
}
x = (x << 1) | bitval;
bitstogo--;
} else {
x = (x << bitstogo);
fputc(x, ofp);
}
return 0;
}
void Assign_Code(node* tree, int c[], int n, string * s) {
int i;
static int cnt = 0;
string buf = malloc(ASCII);
if ((tree->left == NULL) && (tree->right == NULL)) {
for (i = 0; i < n; i++) {
sprintf(buf, "%s%d", buf, c[i]);
}
s[cnt] = buf;
cnt++;
} else {
c[n] = 1;
n++;
Assign_Code(tree->left, c, n, s);
c[n - 1] = 0;
Assign_Code(tree->right, c, n, s);
}
}
node* create(char a[], int x) {
node* ptr;
ptr = (node *) malloc(sizeof(node));
ptr->freq = x;
strcpy(ptr->ch, a);
ptr->right = ptr->left = NULL;
return (ptr);
}
void sort(node* a[], int n) {
int i, j;
node* temp;
for (i = 0; i < n - 1; i++)
for (j = i; j < n; j++)
if (a[i]->freq > a[j]->freq) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
void sright(node* a[], int n) {
int i;
for (i = 1; i < n - 1; i++)
a[i] = a[i + 1];
}
If your program is crashing on what is otherwise a valid operation (like returning from a function or closing a file), I'll near-guarantee it's a buffer overflow problem rather than a memory leak.
Memory leaks just generally mean your mallocs will eventually fail, they do not mean that other operations will be affected. A buffer overflow of an item on the stack (for example) will most likely corrupt other items on the stack near it (such as a file handle variable or the return address from main).
Probably your best bet initially is to set up a conditional breakpoint on writes to the file handles. This should happen in the calls to fopen and nowhere else. If you detect a write after the fopen calls are finished, that will be where your problem occurred, so just examine the stack and the executing line to find out why.
Your first problem (this is not necessarily the only one) lies here:
c = 0;
for (a = 0; a < ASCII; a++) {
if (key[a] != -1) {
array[c] = create(&key[a], value[a]);
numofKeys = c; // DANGER,
c++; // WILL ROBINSON !!
}
}
string code_string[numofKeys];
You can see that you set the number of keys before you increment c. That means the number of keys is one less than you actually need so that, when you access the last element of code_string, you're actually accessing something else (which is unlikely to be a valid pointer).
Swap the numofKeys = c; and c++; around. When I do that, I at least get to the bit printing here1 and exit without a core dump. I can't vouch for the correctness of the rest of your code but this solves the segmentation violation so anything else should probably go in your next question (if need be).
I can see one problem:
strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);
the ch field of struct link is a char array of size 255. It is not NUL terminated. So you cannot copy it using strcpy.
Also you have:
ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");
If small1.txt.huff does not exist, it will be created. But if small1.txt it will not be created and fopen will return NULL, you must check the return value of fopen before you go and read from the file.
Just from counting, you have 4 separate malloc calls, but only one free call.
I would also be wary of your sprintf call, and how you are actually mallocing.
You do an sprintf(buf, "%s%d", buf, c[i]) but that can potentially be a buffer overflow if your final string is longer than ASCII bytes.
I advise you to step through with a debugger to see where it's throwing a segmentation fault, and then debug from there.
i compiled the program and ran it with it's source as that small1.txt file and got "can't open (null)" if the file doesn't exist or the file exist and you give it on the command like ./huf small1.txt the program crashes with:
Program terminated with signal 11, Segmentation fault.
#0 0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
195 if (a[i]->freq > a[j]->freq) {
(gdb) backtrace
#0 0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
#1 0x080489ba in main (argc=2, argv=0xbfd79b64) at huf.c:99
to get this from gdb you run
ulimit -c 100000000
./huf
gdb --core=./core ./huf
and type backtrace
You have various problems in your Code:
1.- mallocs (must be):
//Arrays
char *key = (char*) malloc(ASCII * sizeof(char));
int *value = (int*) malloc(ASCII * sizeof(int));
sizeof(char) == 1, sizeof(char *) == 4 or 8 (if 64 bits compiler is used).
2.- Buffer sizes 255 (ASCII) is too short to receive the contents of array[0]->ch + array[1]->ch + '\0'.
3.- Use strncpy instead of strcpy and strncat instead of strcat.
4.- key is an array of individuals chars or is a null terminated string ?, because you are using this variable in both ways in your code. In the characters counting loop you are using this variables as array of individuals chars, but in the creation of nodes you are passing the pointer of the array and copying as null terminated array.
5.- Finally always check your parameters before used it, you are checking if argc < 2 after trying to open argv[1].