I defined a display function to display the content of a queue:
void display(queue_t* s) {
queue_t* c = s;
int i = c->size;
while (i > 0) {
const char* elem = dequeue(c).value;
printf("Item n°%d : %s\n",i,elem);
i--;
};
};
where queue_t is defined as follow:
typedef struct queue {
node_t* head;
node_t* tail;
int size;
} queue_t;
and dequeue is a function that removes a node from the queue and frees it. This function works as intended.
The function display should display the content of the queue without deleting its content but when testing it, the queue is empty if I call display before removing each element one by one by hand by calling dequeue. I thought that queue_t* c = s; would copy the element without any link between c and s.
How can I copy the content of s into c without any link between the two variables ?
EDIT - MWE
Header file - queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
typedef struct element {
bool type;
const char* value;
} element_t;
typedef struct node {
element_t content;
struct node* previous;
struct node* next;
} node_t;
typedef struct queue {
node_t* head;
node_t* tail;
int size;
} queue_t;
queue_t* init_queue(void);
node_t* init_node(element_t e);
void queue(queue_t* s, element_t e);
element_t dequeue(queue_t* s);
void display(queue_t* s);
#endif
Source code - queue.c
#include "queue.h"
queue_t* init_queue(void) {
queue_t* new = (queue_t*)malloc(sizeof(queue_t));
new->head = NULL;
new->tail = NULL;
new->size = 0;
return new;
};
node_t* init_node(element_t e) {
node_t* new = (node_t*)malloc(sizeof(node_t));
new->content = e;
new->next = NULL;
new->previous = NULL;
return new;
};
void queue(queue_t* s, element_t e) {
node_t* n = init_node(e);
if (s->size == 0) {
s->head = n;
s->tail = n;
s->size = 1;
} else {
n->previous = s->tail;
s->tail = n;
s->size++;
};
};
element_t dequeue(queue_t* s) {
if (s->size == 0) {
element_t empty;
empty.type = true;
empty.value = "0";
return empty;
} if (s->size == 1) {
element_t c = s->head->content;
node_t* old = s->head;
s->head = NULL;
s->size = 0;
s->tail = NULL;
free(old);
return c;
} else {
element_t c = s->tail->content;
node_t* old = s->tail;
s->tail = s->tail->previous;
s->tail->next = NULL;
s->size--;
free(old);
return c;
};
};
void display(queue_t* s) {
queue_t* c = s;
int i = c->size;
while (i > 0) {
const char* elem = dequeue(c).value;
printf("Item n°%d : %s\n",i,elem);
i--;
};
};
Test file - test.c
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include "queue.h"
int main(void) {
element_t e1 = {.type = false, .value = "1"};
element_t e2 = {.type = false, .value = "5"};
element_t e3 = {.type = false, .value = "10"};
queue_t* test = init_queue();
queue(test,e1);
queue(test,e2);
queue(test,e3);
display(test);
element_t e4 = dequeue(test);
printf("%s\n",e4.value);
element_t e5 = dequeue(test);
printf("%s\n",e5.value);
element_t e6 = dequeue(test);
printf("%s\n",e6.value);
element_t e7 = dequeue(test);
printf("%s\n",e7.value);
return 0;
}
Run the test file using
gcc -g -std=c99 -Wall -o test.o -c test.c
gcc -g -std=c99 -Wall -o queue.o -c queue.c
gcc -g -std=c99 -Wall -o test queue.o test.o
A few issues ...
You're leaking memory.
Passing structs by value doesn't scale.
queue does not set next for s->tail so forward list traversal doesn't work.
queue is much more complicated than it needs to be
dequeue should be split into two functions: dequeue and destroy
display should just display a list by traversal.
init_node should do a deep copy of content.value
You're leaking memory.
dequeue doesn't free old->value.
It returns a copy that has a valid value.
But, in display, you do:
const char *elem = dequeue(c).value;
You never free elem
Here is the valgrind output:
==2168790== Memcheck, a memory error detector
==2168790== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2168790== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2168790== Command: ./fix1
==2168790==
Item n°3 : 10
Item n°2 : 5
Item n°1 : 1
0
0
0
0
==2168790==
==2168790== HEAP SUMMARY:
==2168790== in use at exit: 24 bytes in 1 blocks
==2168790== total heap usage: 5 allocs, 4 frees, 4,216 bytes allocated
==2168790==
==2168790== LEAK SUMMARY:
==2168790== definitely lost: 24 bytes in 1 blocks
==2168790== indirectly lost: 0 bytes in 0 blocks
==2168790== possibly lost: 0 bytes in 0 blocks
==2168790== still reachable: 0 bytes in 0 blocks
==2168790== suppressed: 0 bytes in 0 blocks
==2168790== Rerun with --leak-check=full to see details of leaked memory
==2168790==
==2168790== For lists of detected and suppressed errors, rerun with: -s
==2168790== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
dequeue returns "by value" -- this doesn't scale
Add int data[10000000]; to element_t and watch the stack blow up ;-)
In general, passing around a struct by value has similar issues in other places.
dequeue should just dequeue the element.
Do one thing well. dequeue does a dequeue and a [partial/incomplete] destroy.
We should have a separate function destroy that fully frees the node_t/element_t
And, dequeue [as written] should be renamed as pop or dequeue_back because it works from tail to head.
Also, we'd probably like a dequeue_front that works from head to tail
And, it's much more useful if the functions return pointers to the dequeued elements and let the caller use destroy on them when the caller is finished with them.
display should just display the queue and not alter/destroy it.
It could be split [after fixup] into two functions (e.g.) display_reverse and display_forward
Here is the refactored code. It is annotated:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifndef DEEPCOPY
#define DEEPCOPY 1
#endif
typedef struct element {
bool type;
#if DEEPCOPY
char *value;
#else
const char *value;
#endif
} element_t;
typedef struct node {
element_t content;
struct node *previous;
struct node *next;
} node_t;
typedef struct queue {
node_t *head;
node_t *tail;
int size;
} queue_t;
queue_t *init_queue(void);
node_t *init_node(const element_t *e);
void queue(queue_t *s, const element_t *e);
node_t *dequeue_back(queue_t *s);
void display_forward(const queue_t *s);
void display_reverse(const queue_t *s);
// prevent/reduce unintential "use after free"
#define FREE(_ptr) \
do { \
free(_ptr); \
_ptr = NULL; \
} while (0)
queue_t *
init_queue(void)
{
queue_t *new = malloc(sizeof(queue_t));
new->head = NULL;
new->tail = NULL;
new->size = 0;
return new;
}
node_t *
init_node(const element_t *e)
{
node_t *new = malloc(sizeof(node_t));
new->content = *e;
// NOTE/FIX: we should deep copy the element
#if DEEPCOPY
new->content.value = strdup(e->value);
#endif
new->next = NULL;
new->previous = NULL;
return new;
}
void
queue(queue_t *s, const element_t *e)
{
node_t *n = init_node(e);
if (s->size == 0) {
s->head = n;
s->tail = n;
s->size = 1;
}
else {
n->previous = s->tail;
// NOTE/BUG: without this forward traversal doesn't work
#if 1
s->tail->next = n;
#endif
s->tail = n;
s->size++;
}
}
void
destroy_element(element_t *elem)
{
#if DEEPCOPY
FREE(elem->value);
#endif
}
node_t *
destroy_node(node_t *node)
{
destroy_element(&node->content);
FREE(node);
// convenience to caller
return node;
}
node_t *
dequeue_back(queue_t *s)
{
node_t *ret = s->tail;
if (ret != NULL) {
s->tail = ret->previous;
if (s->head == ret)
s->head = ret->next;
s->size -= 1;
}
return ret;
}
void
display_forward(const queue_t *s)
{
int i = 0;
const node_t *node = s->head;
for (; node != NULL; node = node->next) {
printf("Item n°%d : %s\n", i, node->content.value);
i++;
}
}
void
display_reverse(const queue_t *s)
{
int i = s->size;
const node_t *node = s->tail;
for (; node != NULL; node = node->previous) {
printf("Item n°%d : %s\n", i, node->content.value);
i--;
}
}
int
main(void)
{
element_t e1 = {.type = false,.value = "1" };
element_t e2 = {.type = false,.value = "5" };
element_t e3 = {.type = false,.value = "10" };
queue_t *test = init_queue();
queue(test, &e1);
queue(test, &e2);
queue(test, &e3);
printf("display_reverse:\n");
display_reverse(test);
printf("display_forward:\n");
display_forward(test);
for (int i = 4; i <= 7; ++i) {
node_t *node = dequeue_back(test);
if (node == NULL)
break;
printf("main: %s (dequeue_back)\n", node->content.value);
destroy_node(node);
}
#if 1
FREE(test);
#endif
return 0;
}
In the above code, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k
Here is the program output:
display_reverse:
Item n°3 : 10
Item n°2 : 5
Item n°1 : 1
display_forward:
Item n°0 : 1
Item n°1 : 5
Item n°2 : 10
main: 10 (dequeue_back)
main: 5 (dequeue_back)
main: 1 (dequeue_back)
I thought that queue_t* c = s; would copy the element without any link between c and s.
No. It only makes a copy of the pointer s that you passed in the function.
Copying would require you to define "copy semantics", in other words:
What happens if you copy a queue, will the "elements" in your queue be copied too? (This is trivial for primitive types, but not so easy for objects).
If your elements are objects, what copy semantic do they have?
(in the case of strings, you'll need something like strncpy or memcpy to copy all characters into a new buffer)
Also, making a "true" (truly independent) copy you will need to make a complete copy of the entire queue.
This means setting up a completely new queue with all needed element data copied.
I'm phrasing it this way, because you certainly cannot re-use the head, tail, next and previous pointers from your existing queue, otherwise it would not be independent.
And... it's a bad design choice to have a method that is supposedly to be read-only* to modify your queue (data).
To display the contents of your queue you don't need to modify it.
You need to be aware, especially in C, when, how, by whom and for how long your data structures are being accessed. Just a single dangling pointer makes your whole program unpredictable.
*displaying data is considered a read-only operation by most (if not all) programmers.
Related
I have some code to create a simple tree based on pointers to nodes, and would like to write this tree (with its data) to a file and than read it back from file into memory. How can I do this ? Here is my code to create a simple tree:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node
{
void *data;
struct Node *left;
struct Node *right;
};
int main(void)
{
unsigned int_size = sizeof(int);
int data;
/* Tree root node */
struct Node *start = (struct Node*)malloc(sizeof(struct Node));
data = 5;
start->data = malloc(int_size);
memcpy(start->data, &data, int_size);
/* Left node of root */
start->left = (struct Node*)malloc(sizeof(struct Node));
data = 4;
start->left->data = malloc(int_size);
memcpy(start->left->data, &data, int_size);
start->left->left = NULL;
start->left->right = NULL;
/* Right node of root */
start->right = (struct Node*)malloc(sizeof(struct Node));
data = 3;
start->right->data = malloc(int_size);
memcpy(start->right->data, &data, int_size);
start->right->left = NULL;
start->right->right = NULL;
/* Print data */
printf("%d\n",*(int*)(start->data));
printf("%d\n",*(int*)(start->left->data));
printf("%d\n",*(int*)(start->right->data));
return 0;
}
Here a proposal, I use preprocessor macro TYPE to be able to change type and separated functions to read/write an element of that type, I also use function mk to easily create node rather than to duplicate the code as you did for each node.
For the serialization I use a very simple way
'e' indicates an empty node
'n' indicate a non empty node, then I write the value then the left then the right node
I also added pr to easily print a tree as a debug function.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TYPE int
struct Node
{
TYPE data;
struct Node *left;
struct Node *right;
};
/* make a new Node */
struct Node * mk(TYPE v, struct Node * l, struct Node * r)
{
struct Node * n = malloc(sizeof(struct Node));
if (n == NULL) {
fprintf(stderr, "not enough memory\n");
exit(-1);
}
n->data = v;
n->left = l;
n->right = r;
return n;
}
/* write/read data */
void wrData(TYPE v, FILE * fp)
{
fprintf(fp, "%d", v); /* dedicated to int */
}
TYPE rdData(FILE * fp)
{
TYPE v;
fscanf(fp, "%d", &v); /* dedicated to int */
return v;
}
/* serialize a tree */
void wr(struct Node * n, FILE * fp)
{
if (n == NULL)
fputc('e', fp);
else {
fputc('n', fp);
wrData(n->data, fp);
wr(n->left, fp);
wr(n->right, fp);
}
}
/* unserialize a tree */
struct Node * rd(FILE * fp)
{
switch (fgetc(fp)) {
case 'e':
return NULL;
case 'n':
{
TYPE v = rdData(fp);
struct Node * l = rd(fp);
return mk(v, l, rd(fp));
}
default:
fprintf(stderr, "invalid file");
exit(-1);
}
}
/* for debug, show tree */
void pr(struct Node * t)
{
if (t == NULL)
printf("()");
else {
putchar('(');
pr(t->left);
putchar(' ');
wrData(t->data, stdout);
putchar(' ');
pr(t->right);
putchar(')');
}
}
/* free a tree */
void del(struct Node * t)
{
if (t != NULL) {
del(t->left);
del(t->right);
free(t);
}
}
int main()
{
/* Tree root node */
struct Node *start = mk(5, mk(4, NULL, NULL), mk(3, NULL, NULL));
/* show it */
pr(start);
putchar('\n');
/* serialize */
FILE * fp;
if ((fp = fopen("/tmp/t", "w")) == 0) {
fprintf(stderr, " cannot open /tmp/t to write");
exit(-1);
}
wr(start, fp);
fclose(fp);
/* free tree */
del(start);
/* unserialize */
if ((fp = fopen("/tmp/t", "r")) == 0) {
fprintf(stderr, " cannot open /tmp/t to read");
exit(-1);
}
start = rd(fp);
fclose(fp);
/* show it */
pr(start);
putchar('\n');
/* free it */
del(start);
return 0;
}
Compilation and execution :
/tmp % gcc -pedantic -Wextra -Wall t.c
/tmp % ./a.out
((() 4 ()) 5 (() 3 ()))
((() 4 ()) 5 (() 3 ()))
/tmp % cat t ; echo
n5n4een3ee
Execution under valgrind :
/tmp % valgrind ./a.out
==19907== Memcheck, a memory error detector
==19907== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==19907== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==19907== Command: ./a.out
==19907==
((() 4 ()) 5 (() 3 ()))
((() 4 ()) 5 (() 3 ()))
==19907==
==19907== HEAP SUMMARY:
==19907== in use at exit: 0 bytes in 0 blocks
==19907== total heap usage: 8 allocs, 8 frees, 1,280 bytes allocated
==19907==
==19907== All heap blocks were freed -- no leaks are possible
==19907==
==19907== For counts of detected and suppressed errors, rerun with: -v
==19907== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)
There are two common ways for serializing objects having pointers on other objects. First one is to replace the pointer with an offset in the file, second one is to use a simple index. In fact, the offset way is mainly used when the data is used directly from the file (database files for example) while the index way is more used for simple storage.
Here a simple way would be to store the nodes into a file recursively as left_node - right_node - main_node. That way when you store a node you already know the index in the file of its left and right children. A possible implementation is then:
static size_t do_save(FILE *fd, struct Node *node, unsigned data_size, int curr) {
size_t left=0, right=0;
if (node->left != NULL) { // first left sub_hierarchy
left = do_save(fd, node->left, data_size, curr);
curr = left;
}
if (node->left != NULL) { // then right one
right = do_save(fd, node->right, data_size, curr);
curr = right;
}
fwrite(&left, sizeof(left), 1, fd); // store index of left child
fwrite(&right, sizeof(right), 1, fd); // then index of right child
fwrite(node->data, data_size, 1, fd); // then the data
return curr + 1; // and return current index
}
size_t save(FILE *fd, struct Node *root, unsigned data_size) {
size_t nb = do_save(fd, root, data_size, 0);
return nb;
}
Here, an index of 0 means a null pointer, and a non null index is a one based index in the file
To deserialize, as I assume that you want each node to be individually allocated, I would use a temporary array of pointers to keep the actual addresses of the allocated nodes:
struct Node* load(FILE *fd, unsigned data_size) {
size_t nb_elts, left, right;
fseek(fd, 0, SEEK_END); // computer number of nodes in the file
nb_elts = ftell(fd) / (data_size + 2*sizeof(size_t));
struct Node** ptx = malloc(nb_elts * sizeof(*ptx)); // allocate array of pointers
fseek(fd, 0, SEEK_SET);
for(size_t i=0; i<nb_elts; i++) { // loop reading nodes
ptx[i] = malloc(sizeof(struct Node)); // allocate node and data
ptx[i]->data = malloc(data_size);
fread(&left, sizeof(size_t), 1, fd); // extract child indices
fread(&right, sizeof(size_t), 1, fd);
fread(ptx[i]->data, data_size, 1, fd); // read data
ptx[i]->left = (left == 0) ? NULL : ptx[left - 1]; // convert indices to pointers
ptx[i]->right = (right == 0) ? NULL : ptx[right - 1];
}
struct Node *last = ptx[nb_elts - 1];
free(ptx); // free the temporary array
return last; // and return the root node
}
Please consider this code I was tinkering with, it reads a file and loads into a doubly linked list:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Node{
char * data;
struct Node *Next;
struct Node *Prev;
};
struct Doubly_Linked_List{
struct Node *headNode;
struct Node *tailNode;
int LLSize;
};
void InsertAtEnd(struct Doubly_Linked_List *myll, char* data );
void reBalance(struct Doubly_Linked_List *myll);
void PrintLL(struct Doubly_Linked_List *myll);
void append( struct Doubly_Linked_List *myll, char* data);
void PrintLL(struct Doubly_Linked_List *myll){
struct Node *Node = myll->headNode;
int i = 0;
while (Node){
//printf("Node is likely at %ld\n", Node->data);
printf("Index: %d has : %s", i, Node->data);
i++;
Node = Node->Next;
}
}
void reBalance(struct Doubly_Linked_List *myll){
printf("Rebalancing myll\n");
struct Node *Node = myll->headNode;
struct Node *LastNode;
int i = 0;
while (Node){
LastNode = Node;
i++;
Node = Node->Next;
}
myll->LLSize = i;
myll->tailNode = LastNode;
}
void append( struct Doubly_Linked_List *myll, char* data){
if (!myll->headNode){
printf("Inserting at start\n");
struct Node *NewHeadNode = malloc( sizeof(*NewHeadNode) ) ;
NewHeadNode->data = malloc( strlen(data) * sizeof(char) );
strcpy(NewHeadNode->data, data);
NewHeadNode->Prev = NULL;
NewHeadNode->Next = NULL;
myll->headNode = NewHeadNode;
} else {
printf("Inserting at end\n");
InsertAtEnd(myll, data);
}
}
void InsertAtEnd(struct Doubly_Linked_List *myll, char* data ){
//printf("%s was evoked\n", __func__);
reBalance(myll);
struct Node *Node = myll->tailNode;
struct Node *NewTailNode = malloc( sizeof(*NewTailNode) );
NewTailNode->data = malloc( strlen(data) *sizeof(char) );
strcpy(NewTailNode->data, data);
NewTailNode->Next = NULL;
NewTailNode->Prev = Node;
Node->Next = NewTailNode;
reBalance(myll);
}
void FreeMem(struct Doubly_Linked_List *myll){
//printf("%s was evoked\n", __func__);
reBalance(myll);
int i = 0;
struct Node *Node = myll->headNode;
while (Node){
struct Node *NextNode = Node->Next;
//printf("Freeing Node at Index: %d with data : %s\n", i, Node->data);
free(Node);
i++;
Node = NextNode;
}
free(myll);
}
int main(){
char *filename = "/proc/net/dev";
struct Doubly_Linked_List *myll = malloc(sizeof(myll));
FILE *fp = fopen(filename,"r");
if (!fp){
printf("Error!\n");
return 0;
}
char filetext[400];
while (fgets(filetext, 400, fp) ){
append(myll, filetext);
PrintLL(myll);
printf("myll->LLSize: %d\n", myll->LLSize);
}
fclose(fp);
FreeMem(myll);
return 0;
}
It works OK, as in, it enumerates and prints the linked list, and this looks like the file it was asked to read.
However, valgrind seems to indicate I have a memory leak:
==7755== 77 bytes in 1 blocks are definitely lost in loss record 1 of 2
==7755== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7755== by 0x1089FE: append (stack_overflow.c:53)
==7755== by 0x108BF7: main (stack_overflow.c:108)
==7755==
==7755== 494 bytes in 4 blocks are definitely lost in loss record 2 of 2
==7755== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7755== by 0x108AAF: InsertAtEnd (stack_overflow.c:70)
==7755== by 0x108A62: append (stack_overflow.c:60)
==7755== by 0x108BF7: main (stack_overflow.c:108)
==7755==
==7755== LEAK SUMMARY:
==7755== definitely lost: 571 bytes in 5 blocks
==7755== indirectly lost: 0 bytes in 0 blocks
==7755== possibly lost: 0 bytes in 0 blocks
==7755== still reachable: 0 bytes in 0 blocks
==7755== suppressed: 0 bytes in 0 blocks
==7755==
After burning hours into it, it still is leaking - 571 bytes is the best I could leak. I must be doing something I am oblivious to, and would like another set of eyes and tutelage. Thanks!
Update: 1
Thanks! I made a few changes based on the responses and suggestions : switched to calloc, and made sure to free up the text data I was reading from the file.
Happy to report valgrind checks passes this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Node{
char * data;
struct Node *Next;
struct Node *Prev;
};
struct Doubly_Linked_List{
struct Node *headNode;
struct Node *tailNode;
int LLSize;
};
void PrintLL(struct Doubly_Linked_List *myll);
void append( struct Doubly_Linked_List *myll, char* data);
void PrintLL(struct Doubly_Linked_List *myll){
//printf("%s was evoked\n", __func__);
struct Node *Node = myll->headNode;
int i = 0;
while (Node){
//printf("Node is likely at %ld\n", Node->data);
printf("Index: %d has : %s", i, Node->data);
i++;
Node = Node->Next;
}
printf("-----------------------------\n");
}
void Freemem(struct Doubly_Linked_List *myll){
struct Node *Node = myll->headNode;
while (Node){
struct Node *Next = Node->Next;
free(Node->data);
free(Node);
Node = Next;
}
free(myll);
}
void append( struct Doubly_Linked_List *myll, char* data){
if (!myll->LLSize){
printf("Inserting at start\n");
struct Node *NewHeadNode = calloc(5, sizeof(*NewHeadNode) ) ;
NewHeadNode->data = calloc( 1,( strlen(data) +2) );
strcpy(NewHeadNode->data, data);
myll->headNode = NewHeadNode;
myll->tailNode = NewHeadNode;
myll->LLSize = 1;
} else {
//printf("Inserting at end\n");
struct Node *OldTail = myll->tailNode;
struct Node *NewTailNode = calloc(1, sizeof(*NewTailNode) );
NewTailNode->data = calloc( 1,( strlen(data) +2) );
strcpy(NewTailNode->data, data);
// wire the pointers
OldTail->Next = NewTailNode;
NewTailNode->Prev = OldTail;
NewTailNode->Next = NULL;
// adjust our size
myll->LLSize++;
// set new tail node:
myll->tailNode = NewTailNode;;
}
}
int main(){
char *nic = "wlp4s0";
char *filename = "/proc/net/dev";
struct Doubly_Linked_List *myll = calloc(10,sizeof(myll));
FILE *fp = fopen(filename,"r");
if (!fp){
Freemem(myll);
perror("Error!\n");
return 1;
}
char filetext[400];
while (fgets(filetext, 400, fp) ){
append(myll, filetext);
PrintLL(myll);
printf("myll->LLSize: %d\n", myll->LLSize);
}
fclose(fp);
Freemem(myll);
return 0;
}
regarding first leak. in function main(), in this code block:
if (!fp)
{
printf("Error!\n");
return 0;
}
At this point in the code execution, the call to malloc() has already been executed, So the allocated memory needs to be passed to free()
as an alternative, do not call malloc() until after the statement:
char filetext[400];
in function: append() there is a check:
if (!myll->headNode){
however, when called from main() the array myll has not been initialized to anything. The chances of the first (uninitialized) field containing all 0x00 is vanishing lee small.
You might try calling calloc() rather than the first call to malloc() (calloc sets all the allocated memory to all 0x00) The first result is NO head entry is ever generated with valid data
Things go downhill from there.
Please correct
I want to create a list with the following structure:
list.h: Contains function prototypes and defines data types
lt.c: main function to test the list
list.c: actual implementation of the list
When executing it I always get a segfault error. When trying to identify it with gdb it is shown that it is the fault of the following line in lt.c:
list_t *li=list_init();
The rest of my lt.c file looks as follows:
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
int main ( int argc, char *argv [ ], char *envp [ ] )
{
list_t *li=list_init();
//li=list_init();
/* if((li=list_init())==NULL)
{
perror (" Cannot allocate memory" ) ;
exit(-1);
}*/
}
My implementation of the list.c function list_init() is as follows:
list_t *list_init ()
{
list_t* newlist = malloc(sizeof(*newlist));
if (!newlist)
{
perror ("malloc-newlist");
exit (EXIT_FAILURE);
}`enter code here`
//newlist->first=NULL;
//newlist->last=NULL;
newlist->first = (struct list_elem *) malloc(sizeof(struct list_elem));
newlist->last = (struct list_elem *) malloc(sizeof(struct list_elem));
return newlist;
}
My list.h file is as follows:
struct list_elem {
struct list_elem *next; // Zeiger auf das naechste Element
char *data; // Zeiger auf ein Datenobject
};
typedef struct list {
struct list_elem *first;// erstes Element in der Liste
struct list_elem *last; // letztes Element in der Liste
} list_t;
/* function prototypes */
list_t *list_init ();
However, I do not know how I could change the implementation so that it does not occur anymore.
Thank you very much for your help.
While it is impossible to tell where your problem lies exactly, I suspect it lies in one of two places. One, you are initializing each data member with a string literal which is read-only on all but a very few systems. So if anywhere in your code you attempt to modify data you could expect a SegFault. The same would apply if you later attempt to free (pointer->data);
Two, you fail of assign your node->next pointers correctly leading to your traversal attempting to derefernce a NULL pointer or indeterminate pointer leading to the same SegFault. This can occur if your append function fails to handle the if (!list->first) { ... } case correctly or the else case where you will be required to set pointer->next = newnode;
There is really no way to tell unless you post a A Minimal, Complete, and Verifiable Example (MCVE), but given list operations are somewhat generic, you could correct the shortcomings with something similar to the following for your init() and append() functions (with an added print() and free() functions thrown in for good measure), e.g.
#include <stdio.h>
#include <stdlib.h>
struct list_elem {
struct list_elem *next; // Zeiger auf das naechste Element
char *data; // Zeiger auf ein Datenobject
};
typedef struct list {
struct list_elem *first;// erstes Element in der Liste
struct list_elem *last; // letztes Element in der Liste
} list_t;
/* function prototypes */
list_t *list_init ();
struct list_elem *list_append (list_t *list, char *data);
void list_print (list_t *list);
void list_free (list_t *list);
int main (void)
{
list_t *li = list_init();
if (list_append (li, (char[]){"erstes"}) == NULL ||
list_append (li, (char[]){"zweites"}) == NULL ||
list_append (li, (char[]){"drittes"}) == NULL) {
perror ("Cannot allocate memory" ) ;
exit (EXIT_FAILURE);
}
list_print (li);
list_free (li);
exit (EXIT_SUCCESS);
}
list_t *list_init (void)
{
list_t *newlist = malloc (sizeof *newlist);
if (!newlist) {
perror ("malloc-newlist");
exit (EXIT_FAILURE);
}
newlist->first = NULL;
newlist->last = NULL;
return newlist;
}
struct list_elem *list_append (list_t *list, char *data)
{
struct list_elem *node = NULL;
if (!list)
return NULL;
if (!(node = malloc (sizeof *node))) {
perror ("malloc-node");
return NULL;
}
node->data = data;
node->next = NULL;
if (!list->first)
list->first = node;
else {
struct list_elem *iter = list->first;
while (iter->next)
iter = iter->next;
iter->next = node;
}
return (list->last = node);
}
void list_print (list_t *list)
{
struct list_elem *iter = NULL;
if (!list)
return;
iter = list->first;
while (iter) {
printf ("%s\n", iter->data);
iter = iter->next;
}
}
void list_free (list_t *list)
{
struct list_elem *iter = NULL;
if (!list)
return;
iter = list->first;
while (iter) {
struct list_elem *victim = iter;
iter = iter->next;
free (victim);
}
free (list);
}
Example Use/Output
$ ./bin/ll_list_elem
erstes
zweites
drittes
Memory Use/Error Check
There is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/ll_list_elem
==22383== Memcheck, a memory error detector
==22383== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==22383== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==22383== Command: ./bin/ll_list_elem
==22383==
erstes
zweites
drittes
==22383==
==22383== HEAP SUMMARY:
==22383== in use at exit: 0 bytes in 0 blocks
==22383== total heap usage: 4 allocs, 4 frees, 64 bytes allocated
==22383==
==22383== All heap blocks were freed -- no leaks are possible
==22383==
==22383== For counts of detected and suppressed errors, rerun with: -v
==22383== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Using last pointer with Forward-Chaining
Since you have a last pointer there is no need for a generic iteration of the list to find the last pointer. I suspect you are intending to use forward-chaining. In that case, you can simply modify append() as follows:
if (!list->first)
list->first = node;
else
list->last->next = node;
(note: the list->last = node assignment is handled in the return)
i think you are not allocate memory in proper way.
TList *list_init()
{
TList *newList = (TList *) malloc(sizeof(TList));
newList->first = (struct list_elem *) malloc(sizeof(structlist_elem));
newList->last = (struct list_elem *) malloc(sizeof(struct list_elem));
newList->first->next= NULL;
newList->last->next= NULL;
return newList;
}
I'm new to C, and I think there may be an issue with pointers here. Any help would be appreciated!
I have a linkedlist struct that looks like this:
ll.h:
#ifndef LLTEST_LL_H
#define LLTEST_LL_H
#include <stdlib.h>
typedef struct _listNode {
void *data;
struct _listNode *next;
} listNode;
typedef struct {
int logicalLength;
int elementSize;
listNode *head;
listNode *tail;
} linkedlist;
typedef struct table {
const char* name;
size_t col_count;
size_t length;
} table;
typedef struct db {
const char* name;
size_t table_count;
table** tables;
} db;
void list_append(linkedlist *list, void *element);
void create_list(linkedlist *list, int elementSize);
void create_db(const char* db_name, db** db);
#endif //LLTEST_LL_H
main.c
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "ll.h"
linkedlist databases_list;
void create_list(linkedlist *list, int elementSize)
{
list->logicalLength = 0;
list->elementSize = elementSize;
list->head = NULL;
list->tail = NULL;
}
void list_append(linkedlist *list, void *element)
{
listNode *node = malloc(sizeof(listNode));
node->data = malloc(list->elementSize);
node->next = NULL;
memcpy(node->data, element, list->elementSize);
if(list->logicalLength == 0) {
list->head = list->tail = node;
} else {
list->tail->next = node;
list->tail = node;
}
list->logicalLength++;
}
listNode* find_database_node(char *name){
listNode *node = databases_list.head;
//bool result = true;
listNode *found_node = NULL;
while(node != NULL) {
db *item = (db *)node->data;
if (strcmp(item->name, name) == 0){
found_node = node;
break;
}
node = node->next;
}
return found_node;
}
void get_db_pool(char *name, db *value){
listNode *node = find_database_node(name);
if(node != NULL){
value = (db *)node->data;
}
else{
value = NULL;
}
}
void set_db_pool(db* value){
list_append(&databases_list, (void *)value);
}
void create_db(const char* db_name, db** db) {
if (*db == NULL) {
*db = malloc(sizeof(db));
}
(*db)->name = db_name;
(*db)->table_count = 0;
(*db)->tables = NULL;
}
int main() {
create_list(&databases_list, sizeof(db *));
char* db_name= "mydb";
db* db1 = NULL;
create_db(db_name, &db1);
set_db_pool(db1); //<--this line
return 0;
}
On the line that I have marked "<--this line", when I check (db)databases_list.head->data's name parameter, I see "\222\017" instead of "mydb" as I would expect (such as when I check db1->name). What am I doing wrong?
I've taken the revised code and edited a bit to suit some of my prejudices, so my line numbers are probably slightly different from yours. When I run it under valgrind, I get a complaint:
==55831== Invalid write of size 8
==55831== at 0x100000EC7: main (ll17.c:78)
==55831== Address 0x100a7c350 is 8 bytes after a block of size 8 alloc'd
==55831== at 0x1000066F1: malloc (vg_replace_malloc.c:303)
==55831== by 0x100000EB9: main (ll17.c:73)
==55831==
==55831== Invalid write of size 8
==55831== at 0x100000ECF: main (ll17.c:78)
==55831== Address 0x100a7c348 is 0 bytes after a block of size 8 alloc'd
==55831== at 0x1000066F1: malloc (vg_replace_malloc.c:303)
==55831== by 0x100000EB9: main (ll17.c:73)
Line 73 is as shown:
void create_db(const char* db_name, db** db) {
if (*db == NULL) {
*db = malloc(sizeof(db)); // 73
}
This allocates enough space for a pointer (strictly, a pointer to a pointer), not for a db structure.
You should avoid using variables with the same name as their (base) type — it confuses everyone except the compiler.
You really need:
void create_db(const char* db_name, db** db) {
if (*db == NULL) {
*db = malloc(sizeof(**db));
}
With that change in place, the code runs OK under valgrind. According to my build of valgrind, it leaks a lot, but I've recently upgraded from Mac OS X 10.10 Yosemite to 10.11 El Capitan, and I don't trust my suppressions file to give me any useful information. It was built under Yosemite, and I'm also getting 'unknown fcntl calls' tracked by valgrind.
I think the line int your test code right here might be flawed.
create_list(&databases_list, sizeof(db *), NULL);
when you do sizeof(db *) you are actually getting the size of the pointer NOT the db struct. You should actually be doing sizeof(db). Since the element size only gets set to the size of a pointer you don't copy enough data over and when you read back you read corrupted data from memory causing your incorrect values.
This code creates a BST, fills it, and makes an effort to release resources. Two versions of release() are shown below:
typedef struct Node {
int d;
struct Node *left;
struct Node *right;
} Node;
int main() {
Node **tree = NULL;
tree = mkTree();
if (!tree) {
puts("problem\n");
return 1;
}
insert(7, tree);
insert(3, tree);
insert(9, tree);
insert(6, tree);
printTree(*tree);
release(tree);
free(tree);
return 0;
}
/* Make a new binary tree */
Node **mkTree() {
Node **t = malloc(sizeof **t);
return t;
}
/* insert datum d into tree */
bool insert(int d, Node **tree) {
Node *newptr = NULL;
if (tree == NULL) { /*ptr to rootptr NULL */
return false;
}
if (*tree == NULL) {
newptr = buildNode(d);
if (!newptr) {
return false;
}
*tree = newptr;
return true;
}
return insert(d, d < (*tree)->d ? &(*tree)->left : &(*tree)->right);
}
What I don't understand is why valgrind claims all resources are freed in BOTH cases (I and II) below. I try to clear each node using release(), and, at the end of main, I call free(tree) to clear Node **tree, which is declared in main.
I.
/* release resources by passing Node **tree */
void release(Node **tree) {
if (*tree) {
Node *here = *tree;
release(&here->left);
release(&here->right);
}
free(*tree);
}
II.
/* passing Node *tree. this shouldn't free anything, right? */
void release(Node *tree) {
if (tree) {
Node *here = tree;
release(here->left);
release(here->right);
}
free(tree);
}
Despite the choice, running this program with four insertions gives
==5182== HEAP SUMMARY:
==5182== in use at exit: 0 bytes in 0 blocks
==5182== total heap usage: 5 allocs, 5 frees, 60 bytes allocated
What's happening here? Is valgrind just keeping a tally count of the number of malloc's and free's?
Both versions of Release are doing the same thing. One just has an extra (and unnecessary) level of indirection. You can pass a pointer to a function and free that pointer; it is not necessary to pass the address of the variable holding the pointer.
In fact, the call to free does exactly that. It just accepts the pointer value (not the address of the variable holding the pointer).