what is wrong with the following code - c

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;
}

Related

Why is realloc giving me inconsistent behaviour?

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.

"realloc(): invalid next size" when reallocating memory a char** pointer that is a member of a struct

I just started learning C recently, and am having issues figuring out memory allocation. I have spent about the last 2~3 days in my extra time trying to figure this out, but have not found a solution yet. So first, I have two structs:
struct _list {
// arr is an array of string arrays
char **arr;
// recs tracks how many records are in the list
size_t recs;
// arrSizes records the size of each string array in arr
size_t *arrSizes;
};
typedef struct _list list_t;
and
struct _string {
char *string;
// size is used to store strlen
size_t size;
};
typedef struct _string string_t;
I initialize the above structs respectively in the following ways.
list_t:
list_t *NewList() {
list_t *List = NULL;
List = malloc(sizeof(*List));
if (List == NULL) {
fprintf(stderr, "Failed to allocate memory to list structure.\n");
return NULL;
}
List->arr = malloc(sizeof(List->arr));
if (List->arr == NULL) {
free(List);
fprintf(stderr, "Failed to allocate memory to list array.\n");
return NULL;
}
List->arrSizes = malloc(sizeof(List->arrSizes));
if (List->arr == NULL) {
free(List);
fprintf(stderr, "Failed to allocate memory to size array.\n");
return NULL;
}
List->recs = 0;
return List;
}
string_t:
// a string array read in by the program is passed with "char* record"
string_t *NewString(char *record)
{
string_t *String = NULL;
String = malloc(sizeof * String);
if (String == NULL) {
fprintf(stderr, "Failed to allocate memory to string structure.\n");
return NULL;
}
String->size = strlen(record) + 1;
String->string = malloc(String->size);
if (String->string == NULL) {
free(String);
fprintf(stderr, "Failed to allocate memory to string array.\n");
return NULL;
}
strcpy(String->string, record);
return String;
}
I read lines from a file and load them into a "matching results" buffer using something like the following code. Please ignore exits and the fact that I don't have null handling after struct initialization is complete; I will add something more useful later. Also, sorry about the length. I edited quite a bit to produce the smallest example I could think of that reproduces the issue.
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
// Check if File exists
void FileExists(FILE *FilePath) {
if (FilePath == NULL) {
fprintf(stderr, "Error: File not found.\n");
exit(1);
}
}
// Delete a string_t struct
int delString(string_t *Structure)
{
if (Structure != NULL) {
free(Structure->string);
free(Structure);
return 0;
}
return 1;
}
// Allocate memory for additional elements added to members of list_t struct
void AllocList(list_t *List, size_t StrLen)
{
char **ArrStrArr_tmp;
size_t *SizeArr_tmp;
char *StrArr_tmp;
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
StrArr_tmp = malloc(sizeof(*StrArr_tmp) * StrLen);
if ((ArrStrArr_tmp == NULL) || (SizeArr_tmp == NULL)
|| (StrArr_tmp == NULL)) {
fprintf(stderr, "Failed to allocate memory.\n");
exit(1);
}
else {
List->arr = ArrStrArr_tmp;
List->arrSizes = SizeArr_tmp;
(List->arr)[List->recs-1]= StrArr_tmp;
}
}
// Add a record to a buffer
int AddRecord(list_t *List, char *AppendRecord)
{
string_t *line = NewString(AppendRecord);
List->recs++;
AllocList(List, line->size);
(List->arr)[List->recs - 1] = line->string;
(List->arrSizes)[List->recs - 1] = line->size;
delString(line);
return 0;
}
// Sends entire string array to lowercase
void tolowerString(char *UpperString, size_t StrLen)
{
int i;
for (i = 0; i < (int)StrLen; i++) {
UpperString[i] = (char)tolower(UpperString[i]);
}
}
// Attempt to match string in lines from a file; lines with matches are read into a buffer
int main()
{
char line[80];
int PrintedLines = 0;
list_t *ResultList = NewList();
char *MyString = "theme";
char *Filename = "List.txt";
FILE *in = fopen(Filename, "r");
// Check if file exists
FileExists(in);
while (fscanf(in, "%79[^\n]\n", line) == 1)
{
char LookString[80];
strcpy(LookString, line);
LookString[strlen(LookString) - 1] = '\0';
// send lookstring to lowercase
tolowerString(LookString, strlen(LookString));
// add line to buffer ResultList if it contains MyString
if (strstr(LookString, MyString)) {
AddRecord(ResultList, line);
PrintedLines++;
}
}
// If PrintedLines is at zero after the while statement terminates, return in abnormal state
if (PrintedLines == 0) {
fprintf(stderr, "No matches found. Please check your input if you are sure there is a match.\n");
return 1;
}
fclose(in);
return 0;
}
When trying to read the 5th matching record into my buffer, my program crashes at this line in the AllocList function:
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
I get the following message on the version I have posted above:
realloc(): invalid old size
aborted (core dumped)
My guess is that I'm running into an error after some default amount of memory from my initial malloc is used, but I have no clue what is actually causing this. In my actual code I'm printing all sorts of things (pointer sizes, etc.), but I still can't spot anything. What's strange is, before writing this post, I was actually seeing the error:
realloc(): invalid next size
aborted (core dumped)
But I can't reproduce it now for some reason...
I have also read that I should reallocating memory for my list_t struct whenever I add an element to one of it's members, but reallocating it actually doesn't change where or how this program crashes. In any case, I'm not sure how I should be reallocating memory for my struct. To clarify, my questions are:
What is causing this memory issue?
Should I be reallocating memory for my list struct, and how much should I be reallocating given that I'm adding an extra element to the arr and arrSizes members?
As the crash suggests, the line
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
is wrong.
This have it read an uninitialized buffer allocated via malloc(), whose value is indeterminate.
The intension of this line is to re-allocate the array pointed at by List->arr, which is an array of char*.
Therefore, the line should be
ArrStrArr_tmp = realloc(List->arr, sizeof(*ArrStrArr_tmp) * List->recs);
just like the following line, which is re-allocating an array of size_t.
SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
Also I found 2 more points for improvement:
Firstly, the usage of some malloc() in the function NewList are not good.
The function is creating zero-element array, so you won't need space for List->arr and List->arrSizes.
Also note that realloc() accepts NULL as the buffer to re-allocate.
list_t *NewList() {
list_t *List = NULL;
List = malloc(sizeof(*List));
if (List == NULL) {
fprintf(stderr, "Failed to allocate memory to list structure.\n");
return NULL;
}
List->arr = NULL;
List->arrSizes = NULL;
List->recs = 0;
return List;
}
Secondly, you are copying pointer instead of string in AddRecord,
so you have problems of memory leak and potential use-after-free.
It seems the string should be copied:
(List->arr)[List->recs - 1] = line->string;
should be
strcpy((List->arr)[List->recs - 1], line->string);

C pointer and dynamic alloc

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.

C write struct to file

I am trying to save struct data to file. I saved the data this way.
node_trx * trx_list;
trx_list = calloc(1, sizeof(node_trx *));
trx_list->amount = "123123123";
trx_list->currency = 123;
trx_list->next_node = NULL;
if (1 != fwrite(trx_list, length, 1, f)) {
//error
}
free(trx_list);
Here is my struct:
typedef struct {
char amount;
int currency;
struct node_trx * next_node; } node_trx;
Main problem is after i saved struct to file and then after read, when print values, it is printing wrong values. For example: i stored currency as 123, then printed 6788576 this kind of numbers.
here is my reading code:
int read_last_trx_from_file (const char * file_name, node_trx * * trx, unsigned * trx_len)
{
FILE * f;
*trx = NULL;
if (NULL == (f = fopen(tools_get_full_filename_const(file_name), "rb")))
{
return 2; //error
}
size_t fsize;
fseek(f, 0, SEEK_END);
fsize = ftell(f);
fprintf(stdout, "file size: %zd\n", fsize);
if (!fsize)
{
fclose(f);
return 3; //no data
} else {
if (fsize == 1) {
return 3; // no data
}
}
rewind(f);
if (NULL != (*trx = (node_trx *) calloc(1, fsize)))
{
if (1 != fread(*trx, fsize, 1, f))
{
fclose(f);
free(*trx);
return 2; //error
}
}
fclose(f);
*trx_len = fsize;
return 0; //OK }
Main function that calls read function:
int display_trx() {
node_trx * card_data;
if (3 != read_last_trx_from_file(LAST_TRX_OBJECT, &card_data, &data_set_len)) {
if (card_data != NULL) {
printf("%s AMOUNT \n", card_data->amount);
printf("%d CURRENCY \n", &card_data->currency);
}
}
}
After i read this way , when i print amount data, segmentation fault error occurs. so why segment error occured?
And when i print currency, it printing 734364636 this kinda numbers. So why it prints wrong numbers.
Or i only wrote pointer of struct to file?
Please help me guys.
There are two obvious errors in your code.
In struct declaration, the type of amount is char, but when you initialize it in trx_list->amount = "123123123";, you assigned a string, or char[10] array (there is an extra one for NULL terminator).
In function display_trx, second printf, the result of &card_data->currency is int *, not int. If you want to print out currency, why don't follow the first print, use card_data->currency (without &)? You get that large number because you are printing pointer value implicitly converted into int, or the address of currency in card_data.
And there is one error which compiler will not warn you (because it is not syntactically wrong.). As BLUEPIXY said in the comments, when allocating and initializing trx_list, you should really use calloc(1, sizeof(node_trx)). You are allocating space for what pointer trx_list points to, not the pointer itself, so there should not be an asterisk in sizeof.
My suggestion is using a "smart" compiler, such as gcc, and enable warnings. This is a good practice (at least for me). Thank you!

An element of a struct loses its value after a function is called passing another struct as argument

I have a problem that I can't figure out. I have the following files: file_reader.c, file_reader.h, file_writer.c, file_writer.h, test_file_reader.c
I'm working with 'struct' to read and write files. For better understanding I wrote the following code test_file_reader.c:
#include <stdio.h>
#include "file_reader.h"
#include "file_writer.h"
int main ()
{
char *file_path = "/home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml";
struct FileReader *fr = malloc(sizeof(struct FileReader));
file_reader_new (file_path, fr);
show_file_reader_values(fr);
struct FileWriter *fw = malloc(sizeof(struct FileWriter));
fw->file_path = "/tmp/text1.txt";
fw->content = "aaa";
write (fw);
show_file_reader_values(fr);
return 0;
}
void show_file_reader_values(const struct FileReader *fr)
{
printf("==========FILE READER==========\n");
printf("file path: %s\n", fr->file_path);
printf("----------file content---------\n");
printf("content:\n%s\n", fr->content);
printf("----------file content---------\n");
printf("n lines: %d\n", fr->n_lines);
printf("n characters: %d\n", fr->n_characters);
printf("==========FILE READER==========\n\n");
}
The function 'file_reader_new' reads the file and then signs the content, file path, number of lines and number of characters to the 'struct' 'FileReader'.
When I call the function 'show_file_reader_values' in the first time I do not have problems with the content but when I call the function 'write' and then call the function 'show_file_reader_values' again, the content is not the same anymore. The question is that the function 'write' of the file 'file_writer.c' and its struct does not have any relation to the file 'file_reader' and its struct. So, how can a function using another struct change the values of another struct of another file ?
The output:
[freitas#localhost test]$ ./test_file_reader
==========FILE READER==========
file path: /home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml
----------file content---------
content:
<cleaner> <id>k3b</id> <label>k3b</label> <description>Disc writing software</description> <option> <id>log</id> <label>Log</label> <description>Delete the log file which contains information about the last writing session(s).</description> <command>delete</command> <search>glob</search> <path>~/.kde/share/apps/k3b/*.log</path> </option> <option> <id>log2</id> <label>Log</label> <description>Delete the log file which contains information about the last writing session(s).</description> <command>delete</command> <search>glob</search> <path>~/.kde/share/apps/k3b/*.log</path> </option> </cleaner>
----------file content---------
n lines: 1
n characters: 621
==========FILE READER==========
==========FILE READER==========
file path: /home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml
----------file content---------
content:
<cleaner> <id>k��U�N
----------file content---------
n lines: 1
n characters: 621
==========FILE READER==========
Did you see ? In the first call I had the entire output:
<cleaner> <id>k3b</id> <label>k3b</label> <description>Disc wri...
but in the second call I had:
<cleaner> <id>k��U�N
file_reader.c
#include <stdio.h>
#include <stdlib.h>
#include "file_reader.h"
int file_reader_new(const char *file_path, struct FileReader *fr)
{
char *content; // holds the file content.
int counter; // holds the file number of lines.
size_t i; // indexing into content.
size_t buffer_size; // size of the content.
char *temp; // for realloc().
char c; // for reading from the input.
FILE *input; // our input stream.
if ((input = fopen(file_path, "r")) == NULL) {
fprintf(stderr, "Error opening input file %s\n", file_path);
exit(EXIT_FAILURE);
}
/* Initial allocation of content */
counter = 0;
i = 0;
buffer_size = BUFSIZ;
if ((content = malloc(buffer_size)) == NULL) {
fprintf(stderr, "Error allocating memory (before reading file).\n");
fclose(input);
}
while ((c = fgetc(input)) != EOF) {
/* Enlarge content if necessary. */
if (i == buffer_size) {
buffer_size += BUFSIZ;
if ((temp = realloc(content, buffer_size)) == NULL) {
fprintf(stderr, "Ran out of core while reading file.\n");
fclose(input);
free(content);
exit(EXIT_FAILURE);
}
content = temp;
}
/* Add input char to the content. */
content[i++] = c;
/* If the character is a break of line
* then the counter will be incremented.
*/
if (c == '\n')
counter++;
}
/* Test if loop terminated from error. */
if (ferror(input)) {
fprintf(stderr, "There was a file input error.\n");
free(content);
fclose(input);
exit(EXIT_FAILURE);
}
/* Make the content a bona-fide string. */
if (i == buffer_size) {
buffer_size += 1;
if ((temp = realloc(content, buffer_size)) == NULL) {
fprintf(stderr, "Ran out of core (and only needed one more byte too ;_;).\n");
fclose(input);
free(content);
exit(EXIT_FAILURE);
}
content = temp;
}
content[i] = '\0';
/* Assigns the variables to the corresponding
* element of the struct.
*/
fr->file_path = file_path;
fr->content = content;
fr->n_lines = counter;
fr->n_characters = i;
/* Clean up. */
free(content);
fclose(input);
return 0;
}
file_reader.h
#ifndef FILE_READER_H_
#define FILE_READER_H_
typedef struct FileReader
{
char *content; // holds the file content.
char *file_path; // holds the file path.
int *n_lines; // holds the number of lines.
int *n_characters; // holds the number of characters.
} FileReader;
// file_reader_new - reads the file
int file_reader_new(const char *file_path, struct FileReader *fr);
#endif
file_writer.c
#include <stdio.h>
#include "file_writer.h"
void write (struct FileWriter *fw)
{
FILE *f = fopen(fw->file_path, "w");
if (f == NULL)
{
printf("Error opening file!\n");
exit(1);
}
fprintf(f, "%s", fw->content);
fclose(f);
}
file_writer.h
#ifndef FILE_WRITER_H_
#define FILE_WRITER_H_
typedef struct FileWriter
{
char *file_path;
char *content;
int *error;
} FileWriter;
#endif
Can you help me ? Thanks!
struct FileReader *fr = malloc(sizeof(struct FileReader));
There is no need to do this. All you need is this:
struct FileReader fr;
Same here:
struct FileWriter fw;
Then just pass the address of these variables to the requisite function(s).
Note this was not given to you as an answer, only as a comment to clean up your code a bit to remove extraneous calls to the heap. It just so happens that the real problem exists elsewhere, and what you're seeing here is undefined behavior in full glory.
I am not sure how are you reading from the file, character by character or block, but anyhow ,
since you update the read data in content buffer, and store the address of content buffer inside file_reader_new() into variable fr->content and immediately releasing the memory will end up loosing the data you read. and lead to condition called Dangling pointer
Dangling pointer
( a pointer variable, which points to a released memory )
that's why its always advised to set the pointer variable after releasing to NULL. Dereferencing a dangling pointer is will lead to Segmentation fault or undefined behavior in some scenarios.
Also, since all you member variables of struct are pointers its better to initialize them to NULL.
you can use calloc to initialize all the variables in a struct, instead of malloc to initialize all the members to NULL, if you are going with dynamic allocation. which goes for string also.
Here is an issue that I see:
fr->content = content;
fr->n_lines = counter;
fr->n_characters = i;
/* Clean up. */
free(content); /* <-- Danger */
You do this in your file_reader_new function. You then call show_file_reader_values and in that function, you're accessing content:
printf("content:\n%s\n", fr->content);
Since you called free() on the content, that pointer no longer points to valid memory, thus undefined behavior occurs.
The fix is to allocate space on fr for the content and copy the characters of content to this space, or simply not call free on content.
So either do this:
fr->content = malloc(i + 1);
strcpy(fr->content, content);
fr->n_lines = counter;
fr->n_characters = i;
/* Clean up. */
free(content);
or this:
fr->content = content;
fr->n_lines = counter;
fr->n_characters = i;
/* No call to free(content) done */

Resources