Deallocated memory functions C - c

I have problems with memory deallocation in C. Without the division of functions everything is OK, but unfortunately it does not work on the same functions. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
char *name;
enum {
summer,
winter
} index;
} student;
bool init(student *s) {
printf("The next stage in the allocation of memory\n");
s = (student*)malloc(sizeof(*s));
if (&s == NULL) {
printf("Allocation Failed\n");
return 0;
} else {
printf("Allocation completed successfully\n");
}
}
void delete(student *s) {
if (&s != NULL) {
printf("begin removal\n");
free(s);
printf("Released memory");
}
}
int main() {
student *s;
init(s);
delete(s);
return 0;
}
I do not know what I'm doing wrong. Please help.

First of all the function init has undefined bbehaviour because it returns nothing in the case of successful memory allocation.
You can check whether the memory was allocated or not by returning pointer to the allocated memory or NULL.
Also this statement
if(&s==NULL){
is wrong. The condition will always yield false because the address of the local variable s is not equal to NULL.
So the function can be rewritten either the following way
student * init()
{
printf("The next stage in the allocation of memory\n");
student *s = ( student* )malloc( sizeof( *s ) );
if ( s == NULL )
{
printf("Allocation Failed\n");
}
else
{
printf("Allocation completed successfully\n");
}
return s;
}
And called like
int main( void )
^^^^^
{
student *s = init();
//...
Or it can be defined the following way
int init( student **s )
{
printf("The next stage in the allocation of memory\n");
*s = ( student* )malloc( sizeof( **s ) );
int success = *s != NULL;
if ( !success )
{
printf("Allocation Failed\n");
}
else
{
printf("Allocation completed successfully\n");
}
return success;
}
and called like
int main( void )
^^^^^
{
student *s;
init( &s );
//...
The function delete should be defined at least like
void delete(student *s) {
if (s != NULL) {
^^^
printf("begin removal\n");
free(s);
printf("Released memory");
}
}

Firstly, free is NULL safe. If variable is NULL already, basically nothing happens. You do not have to check if it is NULL. (you can check page 313 ISO-IEC 9899 )
Also, when you initialize student->name and allocate, there will be memory leak. You have to free that too.
So, your delete function could be like this ;
void delete(student *s) {
printf("begin removal\n");
free(s->name);
free(s);
printf("Released memory");
}
And if (&s == NULL) is wrong. They must be changed with if (s == NULL).
Your allocation may cause really big troubles in the big codes. If you allocate s = (student*)malloc(sizeof(*s)); it means that "allocate s with size of *s". But pointer size is fixed memory block (mostly 8 bytes). It means that you blocks certain size of memory. If you have bigger struct than that, this kind of allocation will corrupt the memory and your executable will be killed by the OS(you can try add some more variables to your struct and initialize them). In small structs and very short runtimes, mostly this allocation works too. But i guarantee that this is not safe for run-time. And it will not give any warnings or errors in compile-time. True way is s = malloc(sizeof(student)). With this way, you exactly allocates all the memory blocks. And your memory stay safe in run-time.
Lastly, your init function should return the initialized variable. And your init function could be like this ;
#define NAME_LENGHT 128
...
student * init(student *s) {
printf("The next stage in the allocation of memory\n");
s = malloc(sizeof(student));
if (s == NULL) {
printf("Allocation Failed\n");
return NULL;
}
s->name = malloc(NAME_LENGHT);
if (s->name == NULL) {
printf("Allocation Failed\n");
return NULL;
} else {
printf("Allocation completed successfully\n");
}
//alternatively you can strdup directly without any allocation
// s->name = strdup("some name");
return s;
}

You never change the s in main.
Solution 1: Pass a pointer to the variable in main for init to populate.
void init_student(student** s_ptr) {
*s_ptr = (student*)malloc(sizeof(student));
if (*s_ptr == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
(*s_ptr)->name = malloc(MAX_NAME_SIZE + 1);
if ((*s_ptr)->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
(*s_ptr)->name[0] = 0;
(*s_ptr)->gpa = 0;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s;
init_student(&s);
delete_student(s);
return 0;
}
Solution 1b: Same, but a cleaner implementation.
void init_student(student** s_ptr) {
student* s = (student*)malloc(sizeof(student));
if (*s_ptr == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name = malloc(MAX_NAME_SIZE + 1);
if (s->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name[0] = 0;
s->gpa = 0;
*s_ptr = s;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s;
init_student(&s);
delete_student(s);
return 0;
}
Solution 2: Return the allocated point to main.
student* init_student() {
student* s = (student*)malloc(sizeof(student));
if (s == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name = malloc(MAX_NAME_SIZE + 1);
if (s->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name[0] = 0;
s->gpa = 0;
return s;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s = init_student();
delete_student(s);
return 0;
}
Note that &s == NULL will never be true because &s is the address of the variable itself. You want just s == NULL to check the value of the pointer in s.

Related

making malloc function doesn't work correctly?

Hi i wrote a simple malloc and free function. The idea is basically from "The C Programming language". Now i have the problem like if I malloc 3 times and i free it in the same order. My allocp is near to the end but the whole memory is free. I give you my example now. My problem is with the 3 frees. Like when i free 4, 3, 2 its ok because you free the allocp down in the correct order, but i don't know how to solve my problem.
Any ideas?
Thanks for your help :)
#include "mystdlib.h"
#include <stdio.h>
#define ALLOCSIZE 1048576 /* Groeße des verfuegbaren Speichers */
static char allocbuf[ALLOCSIZE]; /* Speicher fuer mymalloc */
static char *allocp = allocbuf; /* next free position */
void *mymalloc(size_t size) {
if(allocp + size <= allocbuf + ALLOCSIZE) {
allocp += size;
return (allocp - size);
}
else {
return NULL;
}
}
void myfree(void *ptr) {
if(ptr >= (void*)allocbuf && ptr < (void*)allocbuf + ALLOCSIZE) {
allocp = ptr;
}
}
int main(){
char *buf1 = mymalloc(900000);
if (buf1 == NULL) {
printf("Error 404, allocated memory not found...\n");
return -1;
}
myfree(buf1);
char *buf2 = mymalloc(500000);
char *buf3 = mymalloc(400000);
char *buf4 = mymalloc(300000);
if (buf2 == NULL || buf3 == NULL) {
printf("Fix your mymalloc!\n");
return -1;
}
if (buf4 == NULL) {
printf("This was supposed to happen. Very good!\n");
} else {
printf("Nope. That's wrong. Very wrong.\n");
return -1;
}
myfree(buf2);
myfree(buf3);
myfree(buf4);
char *buf5 = mymalloc(900000);
if (buf5 == NULL) {
printf("You got so far, but your error free journey ends here. Because an error occured.\n");
return -1;
}
char *buf6 = myrealloc(buf5, 500000);
myfree(buf6);
printf("Congrats, you passed all tests. Your malloc and free seem to work\n");
}

Segmentation fault passing struct pointer to function

I'm pretty newb in C and in order to learn about structs I'm building a program which provides a limited set of functionality for a struct ll_string which basically is a linkedlist of strings.
The set of functions I'm trying to implement includes an insert_ll_string() function which should concanate a passed in struct ll_string element to the end of another struct ll_string element but fails to do so because the moment the function is called in my test cases, the program crashes with a sig fault. This is at the STILL WORKS and SIG FAULT comments of the test_insert() function.
This is its header file:
file: ll_string.h
struct ll_string {
char *string;
struct ll_string *next;
};
struct ll_string *create_ll_string(char *, struct ll_string *);
void insert_ll_string(struct ll_string *, struct ll_string *);
void remove_item_from_ll_string(struct ll_string *, struct ll_string *);
void free_ll_string(struct ll_string *);
void print_ll_string(struct ll_string *);
and this is it's corresponding .c file missing a few definitions for functions declared in ll_string.h, but I guess my problem probably only revolves around the functions create_ll_string() and insert_ll_string() anyways.
file: ll_string.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ll_string.h"
/* create_ll_string: allocates memory for a new struct ll_string and
* initializes it with given arguments returns a pointer to new struct */
struct ll_string *create_ll_string(char *string, struct ll_string *next) {
struct ll_string *new_ll_string;
if (!string) {
printf("string can\'t be NULL\n");
return NULL;
}
if (*string == '\0') {
printf("string needs to be at least 1 char long\n");
return NULL;
}
if (!(new_ll_string = (struct ll_string *) malloc(sizeof(struct ll_string)))) {
printf("couldn\'t allocate mem for new ll_string\n");
exit(EXIT_FAILURE);
}
new_ll_string->string = strdup(string);
new_ll_string->next = next;
return new_ll_string;
}
/* insert_ll_string: concanates item to the end of dest */
void insert_ll_string(struct ll_string *dest, struct ll_string *item) {
struct ll_string *cur;
if (!dest) {
printf("dest and item can\'t be NULL\n");
return;
}
if (!item) {
printf("item can\'t be NULL\n");
return;
}
cur = dest;
while (!cur->next) {
cur = cur->next;
}
cur->next = item;
return ;
}
/* remove_item_from_ll_string: removes item from list src */
void remove_item_from_ll_string(struct ll_string *src, struct ll_string *item) {
return ;
}
/* printf_ll_string: prints each string in ll_string */
void print_ll_string(struct ll_string *ll_string) {
if (!ll_string) {
printf("ll_string is NULL\n");
return ;
}
do {
printf("%s\n", ll_string->string);
} while (!(ll_string = ll_string->next));
}
/* free_ll_string: frees all memory pointed to by ll_string */
void free_ll_string(struct ll_string *ll_string) {
struct ll_string *next;
if (!ll_string) {
return ;
}
while ((next = ll_string->next)) {
free(ll_string->string);
free(ll_string);
ll_string = next;
}
}
and here are my tests. Everything works fine until insert_ll_struct() is evoked by the test_insert() function. (test_create() works as expected) Tests are done using the MinUnit framework.
file: tests_ll_string.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "minunit.h"
#include "ll_string.h"
#define MAX_ERROR_MSG_LENGTH 1000
int tests_run = 0;
static char *test_create(void) {
struct ll_string *test_ll;
struct ll_string *test_null_ll;
char *empty_string = strdup("");
char *null_string = NULL;
char *correct_string = strdup("this should work");
char *correct_string2 = strdup("this should also work");
char *error_msg;
if (!(error_msg = (char *) malloc(sizeof(char) * MAX_ERROR_MSG_LENGTH))) {
printf("couldn\'t allocate mem for error msg");
exit(EXIT_FAILURE);
}
// test_ll->string == correct_string
// test_ll->next == NULL
test_ll = create_ll_string(correct_string, NULL);
sprintf(error_msg, "error, test_ll->string != \"%s\" is %s", correct_string, test_ll->string);
mu_assert(
error_msg,
strcmp(test_ll->string, correct_string) == 0);
// test_ll->next->string == correct_string
// test_ll->string == correct_string2
test_ll = create_ll_string(correct_string2, test_ll);
sprintf(error_msg, "error, test_ll->string != \"%s\" is %s", correct_string2, test_ll->string);
mu_assert(
error_msg,
strcmp(test_ll->string, correct_string2) == 0);
sprintf(error_msg, "error, test_ll->next->string != \"%s\" is \"%s\"", correct_string, test_ll->next->string);
mu_assert(
error_msg,
strcmp(test_ll->next->string, correct_string) == 0);
test_null_ll = test_ll;
test_null_ll = create_ll_string(empty_string, test_ll);
// test_null_ll == NULL
mu_assert(
"error, test_null_ll != NULL",
test_null_ll == NULL);
test_null_ll = test_ll;
test_null_ll = create_ll_string(null_string, test_ll);
// test_null_ll == NULL
mu_assert(
"error, test_null_ll != NULL",
test_null_ll == NULL);
sprintf(error_msg, "error, test_ll->string != \"%s\" is \"%s\"", correct_string2, test_ll->string);
mu_assert(
error_msg,
strcmp(test_ll->string, correct_string2) == 0);
sprintf(error_msg, "error, test_ll->next->string != \"%s\" is \"%s\"", correct_string, test_ll->next->string);
mu_assert(
error_msg,
strcmp(test_ll->next->string, correct_string) == 0);
free_ll_string(test_ll);
free(correct_string);
free(correct_string2);
free(empty_string);
free(error_msg);
return 0;
}
static char *test_insert(void) {
struct ll_string *ll_test1;
struct ll_string *ll_test2;
struct ll_string *ll_test3;
char *test_string1 = strdup("test_string1");
char *test_string2 = strdup("test_string2");
char *test_string3 = strdup("test_string3");
char *error_msg;
if (!(error_msg = (char *) malloc(sizeof(char) * MAX_ERROR_MSG_LENGTH))) {
printf("couldn\'t allocate mem for error msg");
exit(EXIT_FAILURE);
}
ll_test1 = create_ll_string(test_string1, NULL);
ll_test2 = create_ll_string(test_string2, NULL);
ll_test3 = create_ll_string(test_string3, NULL);
// STILL WORKS
insert_ll_string(ll_test1, ll_test2); // SEG FAULT
insert_ll_string(ll_test1, ll_test3);
sprintf(error_msg, "error, ll_test1->string != \"%s\" is \"%s\"", test_string1, ll_test1->string);
mu_assert(
error_msg,
strcmp(ll_test1->string, test_string1) == 0);
sprintf(error_msg, "error, ll_test1->next->string != \"%s\" is \"%s\"", test_string2, ll_test1->next->string);
mu_assert(
error_msg,
strcmp(ll_test1->next->string, test_string2) == 0);
sprintf(error_msg, "error, ll_test1->next->next->string != \"%s\" is \"%s\"", test_string1, ll_test1->next->next->string);
mu_assert(
error_msg,
strcmp(ll_test1->next->next->string, test_string3) == 0);
free_ll_string(ll_test1);
free_ll_string(ll_test2);
free_ll_string(ll_test3);
free(test_string1);
free(test_string2);
free(test_string3);
return 0;
}
static char *all_tests(void) {
mu_run_test(test_create);
mu_run_test(test_insert);
return 0;
}
int main(int argc, char* argv[]) {
char *result = all_tests();
if (result != 0) {
printf("%s\n", result);
} else {
printf("ALL TESTS PASSED\n");
}
printf("Tests run: %d\n", tests_run);
return result != 0;
}
and this is the output of compilation and execution:
>> gcc -Wall -o test ll_string.c tests_ll_string.c
>> ./test
string needs to be at least 1 char long
string can't be NULL
[1] 6789 segmentation fault (core dumped) ./test
What's causing this Sigmentation fault? I'm not accessing any memory besides local variables in the section the program crashes. I'm not dereferencing the pointers I'm passing to insert_ll_struct() at least not immediately after the function has been evoked.
Thanks in advance for the help
I think the answer is staring us in the face. In insert_ll_string() :
while (!cur->next) {
should be
while (cur->next) {
I'd look at the logic in free_ll_string(). Are you sure you aren't freeing memory twice? In the code it looks like if frees up all strings in the chain. Therefore I think you will free test_ll multiple times in test_create. See if you still get the error when disabling test_create, and if not then you issue I think is probably resulting from undefined behaviour because you are free-ing things more than once...
It is good practice to set any freed pointer to NULL after freeing the memory that it is pointing to, then you will avoid this problem.
/* free_ll_string: frees all memory pointed to by ll_string */
void free_ll_string(struct ll_string *ll_string) {
struct ll_string *next;
if (!ll_string) {
return ;
}
while ((next = ll_string->next)) {
free(ll_string->string);
free(ll_string);
ll_string = next;
}
}
while (!cur->next) { cur = cur->next; }
This code will cause crash suppose cur->next return null than you are trying to acess null next that is invalid you should put null check for cur in while loop like thiswhile (null!=cur&& !cur->next)

Another way to do cleanup in C?

Consider this program:
int main(void)
{
int* i = malloc(sizeof(int));
int* j = malloc(sizeof(int));
}
However this is a naive approach, because malloc may fail and the pointers are not free'd.
So you can do this:
int main(void)
{
int* i;
int* j;
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
if ((j = malloc(sizeof(int)) < 0)
{
free(i);
return -1;
}
free(i);
free(j);
}
However I consider this very error-prone. Consider having to assign 20 pointers, in the last malloc error case, you have to free 19 variables and then return -1.
I also know atexit, which can help me to write it like this:
int* i;
int* j;
void del_i(void)
{
free(i);
}
void del_j(void)
{
free(j);
}
int main(void)
{
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
else
{
atexit(del_i);
}
if ((j = malloc(sizeof(int)) < 0)
{
return -1;
}
else
{
atexit(del_j);
}
}
Which is better, but I dislike having to declare all pointers as global. Is there some way to combine these two approaches, basically:
Having destructors for pointers, which can be either executed directly or be used with atexit.
Having pointers local to functions.
free on NULL is defined to be a safe no-op. So a non-jumping variation could be:
int *i = malloc(sizeof(int));
int *j = malloc(sizeof(int));
if(i && j)
{
// do some work
}
free(i);
free(j);
First, this will not detect malloc failure:
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
malloc returns NULL on failure, not a negative number.
Second, atexit is good for cleaning up static and global objects. It is not a good idea to make local objects global only to use them inside atexit.
A better approach is to make a struct for all related pointers that you need to allocate in an all-or-nothing unit, define a function for freeing them all at once, and write a function that allocates them one by one with memory checking on each allocation:
typedef struct AllOrNothing {
double *dPtr;
int *iPtr;
float *fPtr;
size_t n;
} AllOrNothing;
void freeAllOrNothing(AllOrNothing *ptr) {
free(ptr->dPtr);
free(ptr->iPtr);
free(ptr->fPtr);
free(ptr);
}
int allocateAllOrNothing(size_t n, AllOrNothing **res) {
*res = malloc(sizeof(AllOrNothing));
if (*res == NULL) {
return -1;
}
// Freeing NULL is allowed by the standard.
// Set all pointers to NULL upfront, so we can free them
// regardless of the stage at which the allocation fails
(*res)->dPtr = NULL;
(*res)->iPtr = NULL;
(*res)->fPtr = NULL;
(*res)->n = n;
(*res)->dPtr = malloc(n*sizeof(double));
if ((*res)->dPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
(*res)->fPtr = malloc(n*sizeof(float));
if ((*res)->fPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
(*res)->iPtr = malloc(n*sizeof(int));
if ((*res)->iPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
return 0;
}
int main(void)
{
int* i = NULL; // Init with NULL otherwise free on none NULL possible
int* j = NULLL;
if (!(i = malloc(sizeof(int)))
{
goto exit;
}
if (!(j = malloc(sizeof(int)))
{
goto exit;
}
...
exit:
free(i);
free(j);
...
return err;
}
This is something you can solve with goto statements.
int main(void)
{
int* i = NULL;
int* j = NULL;
bool success = false;
do {
i = malloc(sizeof(int));
if (NULL == i) break;
j = malloc(sizeof(int));
if (NULL == j) break;
success = true;
} while (0);
if (!success)
{
printf("Something failed!");
}
else
{
printf("All succeeded!");
// Do more work
}
free(i);
free(j);
return (success? 0 : 1);
}
Avoid multiple exit points. Avoid interlacing allocation and error handling. Follows a clean order of operation:
Declare, allocate and initialize resources..
If all successful, do the task.
Clean-up.
Return status.
// Do all allocations first, test their `NULL`-ness, then free them all.
int main(void) {
// Allocate resources
// declare and allocate in one step
int* i = malloc(sizeof *i);
double* j = malloc(sizeof *j);
// Test for acceptability
bool ok = i && j;
// Perform the main body of code
if (ok) {
; // do normal process in the code;
}
// free resources
free(i);
free(j);
// return status
return ok ? 0 : -1;
}
int *i=NULL,*j=NULL;
if(!(i=malloc(sizeof(int))))
goto EXIT;
if(!(j=malloc(sizeof(int))))
goto EXIT;
/* do some work */
return 0;
EXIT:
free(i);
free(j);
exit(EXIT_FAILURE);
Although goto is considered a bad programming practice
but here we can use it to get our task done with ease and simplicity

creating own malloc function in c

I have create my own malloc function and it works properly. but I want to create another malloc using only array without struct. Is that possible to create without struct? This is my code.
#include <stdio.h>
char memory[20000];
int freeMem=20000;
typedef struct{
int start;
int end;
}chunk;
void *MyMalloc(int size){
printf("\nMemory Size= %d ",size);
if(size==0){
printf("0 means no memory\n");
return 0;
}
int memsize=20000;
chunk *p=(chunk *)&memory[0];
if(freeMem >= size+sizeof(chunk)){
while(p<(chunk *)&memory[19999]){
if(p->start==0){
if(p->end !=0){
if(size+sizeof(chunk)< (p->end - (int)p)){
p->start=(int)p+8;
p->end=(int)p+8+size;
freeMem = freeMem-(size+8);
printf("free Mem : %d\n",freeMem);
return (int *)p->start;
}
else{
p=(chunk *)p->end;
continue;
}
}
else{
p->start=(int)p+8;
p->end=(int)p+8+size;
freeMem = freeMem-(size+8);
printf("free Mem : %d\n",freeMem);
return (int *)p->start;
}
}
p = (chunk *)p->end;
}
}
else{
printf("no space...!\n");
return 0;
}
}
void MyFree(void * p){
chunk *ptr = (chunk *)p;
ptr--;
freeMem=freeMem+(ptr->end - ptr->start)+sizeof(chunk);
if(ptr->start != 0){
printf("\nfreed Memory : %d\t",ptr->end - ptr->start);
ptr->start = 0;
}
else{
printf("\nno Such memory allocated!!!!!\n");
}
}
Roughly speaking, the basic mechanism to use would be the same. In MyMalloc, allocate 2*sizeof(int) space more, store the content of a chunk there and return the address behind the 2*sizeof(int). On deallocation, do the same process in reverse - subtract 2*sizeof(int) from the argument to access the content of which was stored in chunk before.

Not sure where there is an unitialized value created by heap allocation

Everything seems to be working fine, memory is allocating and freeing and doing what its supposed to be doing but when I check it with valgrind --track-origins=yes I get this conditional jump after entering in a name and a number.
==25590== Conditional jump or move depends on uninitialised value(s)
==25590== at 0x4007BD: add_car (in /students/5/gmi6y5/cs2050/lab4/a.out)
==25590== by 0x400704: main (in /students/5/gmi6y5/cs2050/lab4/a.out)
==25590== Uninitialised value was created by a heap allocation
==25590== at 0x4A069EE: malloc (vg_replace_malloc.c:270)
==25590== by 0x4006D9: main (in /students/5/gmi6y5/cs2050/lab4/a.out)
typedef struct FreightCars_
{
char *name;
int number;
struct FreightCars_ *next_car;
}FreightCar;
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Insufficient number of arguements\n");
return 0;
}
int size = atoi(argv[1]);
FreightCar *engine = (FreightCar*)malloc(sizeof(FreightCar));
int i;
for(i=0;i<size;i++)
{
printf("Enter in freight car name and number: ");
add_car(engine);
}
free_cars(engine);
return 0;
}
void add_car(FreightCar *engine)
{
FreightCar *newPtr = (FreightCar*)malloc(sizeof(FreightCar));
newPtr->name = malloc(sizeof(char) * MAX_STR_LEN);
if(newPtr == NULL)
{
printf("Unable to allocate memory\n");
exit(1);
}
scanf("%s", newPtr->name);
scanf("%d", &newPtr->number);
newPtr->next_car = NULL;
if(engine->next_car == NULL)
{
engine->next_car = newPtr;
printf("added at the beginning\n");
}
else
{
FreightCar *currentPtr = engine;
while(currentPtr->next_car != NULL)
{
currentPtr = currentPtr->next_car;
}
currentPtr->next_car = newPtr;
printf("added later\n");
}
free(newPtr->name);
}
void free_cars(FreightCar *engine)
{
if(engine == NULL)
{
printf("Linked list is empty now\n");
}
else
{
free_cars(engine->next_car);
}
free(engine);
engine = NULL;
}
In main you do
FreightCar *engine = (FreightCar*)malloc(sizeof(FreightCar));
then in a for loop you call
add_car(engine);
add_car does
if(engine->next_car == NULL)
But as #Oli Charlesworth pointed out in a comment, you have not initialised the memory pointed to by engine, so you are making a decision based on unitialised memory contents here, hence the Valgrind complaint.

Resources