Read access violation when trying to compare int? - c

I am getting a read access violation and I cant understand why. I though I allocated everything correctly and I shouldn't be reading past the end of anything? Any ideas or help is greatly appreciated.
I am trying to read in a file that has a single integer per line and store those into a binary tree and linked list. The tree actually holds the data and the linked list just holds pointers to the nodes in the tree.
The error happens in the insert() function when data and node->num are being compared.
The newNode() function creates both a tree node and a linked list node, it only returns the tree node because of the double pointer handling the linked list.
// Garrett Manley
// delete these when turning it in
#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>
// define structs
struct treeNode {
int num;
struct treeNode *right, *left;
};
struct listNode {
struct treeNode *tNode; // data is a pointer to the tree
struct listNode *next;
};
// creates and returns a new node with given data, also adds node to linked list
struct treeNode *newNode(int data, struct listNode *(*list)) {
// make tree node
struct treeNode *node = malloc(sizeof(struct treeNode));
node->num = data;
node->left = NULL;
node->right = NULL;
// make list node
// insert entry into linked list
struct listNode *newNode;
newNode = malloc(sizeof(struct listNode));
newNode->tNode = node;
newNode->next = *list;
*list = newNode;
return (node);
}
// inserts given node into the tree in sorted order
struct treeNode *insert(struct treeNode *node, int data, struct listNode *(*list)) {
if (node == NULL) { // if the tree is empty return new node
return (newNode(data, list));
} else { // if there is a node use recursion to get to the bottom of the tree and add a node in the right spot
if (data <= node->num) {
node->left = insert(node->left, data, list);
} else {
node->right = insert(node->right, data, list);
}
return (node); // return the (unchanged) node pointer
}
}
// print linked list by looping through list, keep looping until node.next == null
// while looping print like this node.data.data to get the tree nodes data
void printList(struct listNode *(*list)) {
struct listNode *tmp = *list;
while (tmp->next != NULL) {
tmp = tmp->next;
printf("here");
}
}
// skim through the file and find how many entries there are
int SCAN(FILE (*stream)) {
int size = 0;
char *str = malloc(100 * sizeof(char));
while (fgets(str, 100, stream) != NULL) {
size++;
}
return size;
}
// loop through the file and load the entries into the main data array
void LOAD(FILE *stream, int size, struct treeNode *(*tree), struct listNode *(*list)) {
rewind(stream);
int i;
char *tmp = malloc(100 * sizeof(char));
for (i = 0; i < size; i++) {
fgets(tmp, 100, stream);
// recursively call insert to create the list, fix
*tree = insert(*tree, atol(tmp), list);
}
}
// free up everything
void FREE(struct treeNode *BlackBox, int size) {
// free linked list first
// then free the tree nodes, do this recursively
}
int main(int argv, char *argc[]) {
FILE *file = fopen("./hw7.data", "r");
int size = SCAN(file);
struct treeNode *tree = malloc(size * sizeof(struct treeNode));
struct listNode *list = malloc(size * sizeof(struct listNode));
LOAD(file, size, &tree, &list);
// print output
// print linked list
//printList(list);
fclose(file);
return 0;
}

There is no need to allocate memory in main for the tree, nodes are allocated by insert as needed. Allocating uninitialized memory for tree and list causes undefined behavior when the members pointed to by these pointers are dereferenced.
Change the initializations to:
struct treeNode *tree = NULL;
struct listNode *list = NULL;
Also note that the parentheses in return (node); are unnecessary, it is customary to write return node; instead.

Was allocating memory twice, removed malloc() in main() and now program works.

Related

Cannot push element to the head of the linked list

I'm trying to implement linked list in C and I've got a problem with wrong output. The first element seems not to be pushed in the head of the list.
I expect the below output in the program:
1
1
Actual output:
0
0
Have you ideas what's wrong with the code? Thanks
Problematic code:
main.c
#include <stdio.h>
#include "linked_list.h"
int main(void) {
struct ll_node *node = ll_new();
ll_push_front(node, 1);
printf("%d\n", ll_size(node)); // should print 1, actual output: 0
printf("%d\n", ll_pop_back(node)); // should print 1, actual output: 0
ll_destroy(node);
return 0;
}
linked_list.h
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include <stdbool.h>
struct ll_node {
int data;
struct ll_node *prev;
struct ll_node *next;
};
struct ll_node *ll_new();
void ll_destroy(struct ll_node *node);
void ll_push_back(struct ll_node *node, int data);
void ll_push_front(struct ll_node *node, int data);
int ll_pop_back(struct ll_node *node);
int ll_pop_front(struct ll_node *node);
int ll_delete_at(struct ll_node *node, int index);
bool ll_contains(struct ll_node *node, int data);
int ll_find(struct ll_node *node, int data);
int ll_size(struct ll_node *node);
#endif //LINKED_LIST_H
linked_list.c
#include "linked_list.h"
#include <stdio.h>
#include <stdlib.h>
struct ll_node *ll_new() {
struct ll_node* node = (struct ll_node *) malloc(sizeof(struct ll_node *));
if (!node) {
fprintf(stderr, "Error: cannot allocate struct ll_node *node at ll_new");
exit(1);
}
node->prev = NULL;
node->next = NULL;
return node;
}
void ll_destroy(struct ll_node *node) {
while (node->next != NULL) {
struct ll_node *elem = node;
node = node->next;
free(elem);
}
}
void ll_push_back(struct ll_node *node, int data) {
while (node->next != NULL) {
node = node->next;
}
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
current->data = data;
current->next = NULL;
current->prev = node;
node->next = current;
}
void ll_push_front(struct ll_node *node, int data) {
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
current->data = data;
current->next = node;
current->prev = NULL;
node->prev = current;
}
int ll_pop_back(struct ll_node *node) {
struct ll_node *current = node;
while (current->next != NULL) {
current = current->next;
}
int old_data = current->data;
free(current);
return old_data;
}
int ll_pop_front(struct ll_node *node) {
// TODO: implement this
}
int ll_delete_at(struct ll_node *node, int index) {
// TODO: implement this
}
bool ll_contains(struct ll_node *node, int data) {
while (node->next != NULL) {
if (node->data != data) {
return false;
}
node = node->next;
}
return true;
}
int ll_find(struct ll_node *node, int data) {
// TODO: implement this
}
int ll_size(struct ll_node *node) {
int size = 0;
while (node->next != NULL) {
++size;
node = node->next;
}
return size;
}
When you push something on to the front of a list, don't you need to return a pointer to the new list?
Surely this code should be changed to return a pointer to current.
void ll_push_front(struct ll_node *node, int data) {
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
current->data = data;
current->next = node;
current->prev = NULL;
node->prev = current;
}
Then in the calling function, you should be reading that result and storing it as the new list, I think?
struct ll_node *node = ll_new(); allocates a new node (let's call it root), obviously, but then you push a new node into front (let's call it node(1):
root->next = NULL
root->prev = node(1)
node(1)->next = root
node(1)->prev = NULL
In main() node * still points to root, and indeed, it's next pointer is NULL, so ll_size() returns 0. You probably want to return the new root node and do this in main() or pass in the node as struct ll_node ** so you can update it.
node = ll_push_front(node, 1);
Prefer to use the variable rather than type for allocations, and don't cast the void * pointer:
struct ll_node *current = malloc(sizeof *current);
This also fixes the defect of allocating size of a pointer but you require size of the object.
You have a ll_new() function but but ll_push_front() and ll_push_back() implements the same thing again. You should refactor the latter two to use ll_new().
It's not necessarily wrong just confusing that you use struct ll_node to both represent a node of your linked list, and the entire linked list. The root node, for instance, has uninitialized data.
You will need to make similar changes to ll_pop_back(), and you need to update all the relevant pointers.
What should ll_pop_back() do if the list is empty?
ll_destroy() doesn't free the current node.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
struct ll_node {
int data;
struct ll_node *prev;
struct ll_node *next;
};
struct ll_node *ll_new() {
struct ll_node* node = malloc(sizeof *node);
if (!node) {
fprintf(stderr, "Error: cannot allocate struct ll_node *node at ll_new");
exit(1);
}
return node;
}
void ll_push_front(struct ll_node **root, int data) {
if(!root) {
printf("root must be non-NULL");
exit(1);
}
struct ll_node *current = ll_new();
current->data = data;
current->prev = NULL;
current->next = *root;
if(*root)
(*root)->prev = current;
*root = current;
}
int ll_pop_back(struct ll_node **root) {
if(!root) {
printf("root must be non-NULL");
exit(1);
}
if(!*root) {
printf("*root must be non-NULL");
exit(1);
}
struct ll_node *current = *root;
while (current->next != NULL) {
current = current->next;
}
int data = current->data;
if(current->prev)
current->prev->next = NULL;
else
*root = NULL;
free(current);
return data;
}
int ll_size(struct ll_node *node) {
size_t n = 0;
for(; node; node = node->next, n++);
return n;
}
void ll_destroy(struct ll_node *root) {
while(root) {
struct ll_node *tmp = root->next;
free(root);
root = tmp;
}
}
int main(void) {
struct ll_node *root = NULL;
ll_push_front(&root, 'a');
ll_push_front(&root, 'b');
printf("%d\n", ll_size(root));
printf("%c\n", ll_pop_back(&root));
printf("%d\n", ll_size(root));
printf("%c\n", ll_pop_back(&root));
printf("%d\n", ll_size(root));
ll_destroy(root);
}
and the resulting output:
2
a
1
b
0
sizeof (struct node *) != sizeof (struct node):
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
only allocates memory for the pointer, which will likely be 8 bytes on a 64-bit machine or 4 bytes on a 32-bit machine.
Change it to:
struct ll_node *current = malloc (sizeof *current);
Note that the cast is redundant and may hide a bug. There's an implicit conversion to and from a void *.
One advice I see most often on codereview.com is to allocate based on the size of what an object points to rather than the object type itself. This eases maintainability.
The function is also discarding the return value of malloc(). malloc() returns NULL to indicate failure. I'd suggest returning a bool type for ll_push_front(), true would indicate success, and false can indicate a memory allocation failure.
Changes to local parameters are never reflected back in the caller's memory:
The first element seems not to be pushed in the head of the list.
Your push function is very close to being correct. Remember that C has pass-by-value semantics and pointers are not exempt to that rule. As is, your push function only changes its local copy of head. This change will not be reflected back in the main() function. We need the push function to be able to change the caller's memory. The traditional method to allow a function to change it's caller memory is to pass a pointer to the caller's memory instead of a copy.
So instead of:
struct node *head = /* some value */;
push_node (head, /* some value */);
We pass a pointer to the head pointer, so the changes made to the head pointer in the push function are reflected back in main().
push_node (&head, /* some value */);
And dereference the pointer in the push function to access the caller's memory.
There are other issues in your code. I'd suggest implementing a singly linked list before moving on to this.
For starters within the function ll_new there is incorrectly allocated memory for a node of the list.
struct ll_node* node = (struct ll_node *) malloc(sizeof(struct ll_node *));
Instead you need to write either
struct ll_node* node = (struct ll_node *) malloc(sizeof(struct ll_node ));
or
struct ll_node* node = (struct ll_node *) malloc(sizeof( *node ));
The function does not make a great sense because it does not initialize the data member data of the created node.
The same problem with allocation memory exists also in the function ll_push_front
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
Again you need to write either
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node ));
or
struct ll_node *current = (struct ll_node *) malloc(sizeof( *current ));
Actually everywhere in the code you are using incorrect expressions in calls of malloc.
Also the function does not change the pointer to the head node of the list.
It only sets its data member prev to the newly created node
As a result the function ll_size will always return 0 because the data member next of the node pointed to by the pointer node declared in main is equal to NULL
int ll_size(struct ll_node *node) {
int size = 0;
while (node->next != NULL) {
++size;
node = node->next;
}
return size;
}
The function ll_push_back can invoke undefine behavior if a null pointer is passed to the function due to this while loop within teh function
while (node->next != NULL) {
node = node->next;
}
The function ll_pop_back again does not check whether a null pointer is passed
while (current->next != NULL) {
current = current->next;
}
Also if the list contains only one node then the function does not change the passed pointer. The node will be deleted by the pointer that points to the deleted node will stay unchanged.
Or the function ll_destroy does not free all the allocated memory
void ll_destroy(struct ll_node *node) {
while (node->next != NULL) {
struct ll_node *elem = node;
node = node->next;
free(elem);
}
}
leaving the last node in the list undeleted that results in a memory leak due to the condition in the while loop
while (node->next != NULL) {
Your code contains many errors. You need to rewrite it. Start from this declaration in main
struct ll_node *head = NULL;
and try to write at first the function ll_push_front that adds one node to the empty list.
The list shall not have a dummy node.
Pay attention to that it would be better to declare one more structure like for example
struct linked_list {
size_t size;
struct ll_node *head;
struct ll_node *tail;
};
that will indeed specify the double-lonked list.
In this case there will be much efficient to add new nodes to teh tail of the list or to determine the size of the list.
You could define a list in main like
struct linked_list list = { .size = 0, .head = NULL, .tail = NULL };
And a function that adds a node to the beginning of the list could look like
int ll_push_front( struct linked_list *list, int data );
The function can be defined the following way
int ll_push_front( struct linked_list *list, int data )
{
struct ll_node *new_node = malloc( sizeof( *new_node ) );
int success = new_node != NULL;
if ( success )
{
new_node_>data = data;
new_node->next = list->head;
new_node->prev = NULL;
if ( list->head != NULL )
{
list->head->prev = new_node;
}
else
{
list->tail = new_node;
}
list->head = new_node;
++list->size;
}
return success;
}
And the function is called in main like for example
ll_push_front( &list, 1 );
or
if ( !ll_push_front( &list, 1 ) )
{
puts( "Error: not enough memory!" );
}
In turn the function ll_push_back can look the following way
int ll_push_back( struct linked_list *list, int data )
{
struct ll_node *new_node = malloc( sizeof( *new_node ) );
int success = new_node != NULL;
if ( success )
{
new_node_>data = data;
new_node->next = NULL;
new_node->prev = list->tail;
if ( list->tail != NULL )
{
list->tail->next = new_node;
}
else
{
list->head = new_node;
}
list->tail = new_node;
++list->size;
}
return success;
}
To get the size of the list you can write for example
size_t ll_size( const struct linked_list *list )
{
return list->size;
}

How to Use Typedef Structure Name with Pointer

I am trying to implement a linked list using the given structure for a bigger project. The structure is defined below:
typedef struct node {
unint32_t size; // = size of the node
struct node * link; // = .next pointer
} * ListNode;
I was able to implement a linked list using struct node *. But when I attempt to use ListNode like in the following program:
typedef struct node {
unint32_t size;
struct node * link;
} * ListNode;
void insert_node (ListNode * head, unint32_t size) {
ListNode new_node = (ListNode) malloc (sizeof(ListNode));
new_node->size = size;
new_node->link = NULL;
if (head == NULL) {
head = &new_node;
}
else {
ListNode current = *head;
while (current->link != NULL) {
current = current->link;
}
current->link = new_node;
}
}
int main (int argc, char const * argv[]) {
ListNode head = NULL;
insert_node (&head, 10);
insert_node(&head, 20);
ListNode ptr = head;
while (ptr != NULL) {
printf ("%d ", ptr->size);
}
printf ("\n");
return 0;
}
I get a segmentation fault. Why is that? It even says that struct node * and ListNode are incompatible pointers/types. I thought they were the same struct just named differently.
A little clarification
typedef struct node {
unint32_t size;
struct node * link;
} *ListNode;
creates a type called ListNode. It is a pointer to a struct node. It is not a struct node
So when you do
sizeof(ListNode)
you get the size of a pointer, not the size of struct node
You needed to do
sizeof(struct node)
A very common thing to do is this
typedef struct node {
uint32_ size;
struct node* link;
} *PListNode, ListNode;
this creates 2 types
PlistNode which is a pointer to a struct node
ListNode which is a struct node
the 'P' is a reminder that this is a pointer
so now you can do
PListNode pn = malloc(sizeof(ListNode));
Since you supply a struct node** (a ListNode*) to insert_node, you need to dereference it to assign to it.
You malloc the size of a struct node* (a ListNode) but you need to malloc the size of a struct node.
You also need to do ptr = ptr->link in the loop in main.
Example:
void insert_node(ListNode* head, uint32_t size) {
// corrected malloc, you don't want the sizeof a pointer but the
// size of a `node`:
ListNode new_node = malloc(sizeof *new_node);
new_node->size = size;
new_node->link = NULL;
if (*head == NULL) { // corrected check (dereference head)
*head = new_node; // corrected assignment
} else {
ListNode current = *head;
while (current->link != NULL) {
current = current->link;
}
current->link = new_node;
}
}
int main() {
ListNode head = NULL;
insert_node(&head, 10);
insert_node(&head, 20);
// the below loop had no exit condition before:
for(ListNode ptr = head; ptr; ptr = ptr->link) {
printf("%d ", ptr->size);
}
printf("\n");
}
Demo

Fixing load of null pointer of type 'Node *' error

I am working on implementing a Linked List data structure in C. Below are my current functions for my Linked List implementation file (llist.c)
#include "llist.h"
// Frees all allocated memory associated with the list pointers iteratively
void deleteList(Node **list) {
Node* ptr = *list;
Node* temp;
while(ptr != NULL) {
free(ptr->data);
temp = ptr;
ptr=ptr->next;
free(temp);
}
}
// Frees all allocated memory associated with a single node
void deleteNode(Node **toDelete) {
Node * del = *toDelete;
free(del->data);
free(del);
}
// Allocates memory for a new string and returns a pointer to the memory
Node *newNode(char *string) {
unsigned long len = strlen(string);
printf("length : %lu \n\n", len);
Node *temp = (Node*)malloc(sizeof(Node));
temp->data = (char*)malloc(len + 1*sizeof(char));
strcpy(temp->data, string);
temp->next = NULL;
return temp;
}
// Removes a node from the front of a list
Node *pop(Node **list) {
Node *newptr = (*list)->next;
deleteNode(list);
return newptr;
}
// Adds a node to the front of a list
void push(Node **list, Node *toAdd) {
toAdd->next = *list;
*list = toAdd;
}
// Return a list of pointers in order
void reverseOrder(Node **list) {
Node* prev = NULL;
Node* current = *list;
Node* next;
while (current != NULL) {
next = current->next;
current->next = prev;
prev = current;
current = next;
}
*list = prev;
}
// Prints the string stored in a single node
void printNode(Node *singleNode) {
printf("Data : %s", singleNode->data);
}
// Prints an entire linked list. Nodes are printed from first to last
void printLinkedList(Node *linkedList) {
Node *temp = linkedList;
while(temp!=NULL) {
printf("Data : %s", temp->data);
temp = temp->next;
}
}
When testing the implementation in my driver file, I receive the following error
runtime error: load of null pointer of type 'Node *' (aka 'struct listNode *')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior llist.c:49:19
where line 49 corresponds to toAdd->next = *list in the llist.c file
I am struggling to figure out why this error is occurring as I am calling my push function with the appropriate parameters to an initially empty (NULL) linked list.
driver file (testllist.c) for reference:
#include "llist.h"
int main (int argc, char *argv[]) {
printf("argc: %d", argc);
printf("\n\n");
int num_inputs = argc;
Node **list = NULL;
if (argc == 1) {
printf("No arguments passed.\n");
} else {
for (int i = 1; i < num_inputs; i++) {
printf("String is: %s\n", argv[i]);
Node *n = newNode(argv[i]);
printf("String is : %s\n\n", argv[i]);
push(list, n);
printLinkedList(*list);
}
reverseOrder(list);
pop(list);
deleteList(list);
}
return 0;
}
header file (llist.h) where Node data type and functions are defined
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// The listNode data type for storing entries in a linked list
typedef struct listNode Node;
struct listNode {
char *data;
Node *next;
};
// Frees all allocated memory associated with the list pointers iteratively
void deleteList(Node **list);
// Frees all allocated memory associated with a single node
void deleteNode(Node **toDelete);
// Allocates memory for a new string and returns a pointer to the memory
Node *newNode(char *string);
// Removes a node from the front of a list and returns a pointer to said node
Node *pop(Node **list);
// Adds a node to the front of a list
void push(Node **list, Node *toAdd);
// Return a list of pointers in order
void reverseOrder(Node **list);
// Prints the string stored in a single node
void printNode(Node *singleNode);
// Prints an entire linked list. Nodes are printed from first to last
void printLinkedList(Node *linkedList);
As you initialized the pointer list as a null pointer
Node **list = NULL;
then within the function push you may not dereference this pointer
void push(Node **list, Node *toAdd) {
toAdd->next = *list;
*list = toAdd;
}
And the error message reports this problem.
You should declare the pointer like
Node *list = NULL;
and pass it to functions that expect an object of the type Node ** like for example
push( &list, n );
And it would be much better if the function will be declared like
int push( Node **list, const char * );
That is it should report whether a new node was successfully pushed or not and the allocation of the new node should be hidden from the user that calls the function.
Pay attention to that for example the function deleteNode does not make a great sense.
void deleteList(Node **list) {
Node* ptr = *list;
Node* temp;
while(ptr != NULL) {
free(ptr->data);
temp = ptr;
ptr=ptr->next;
free(temp);
}
}
The pointer to the head node of the list is passed by reference. However within the function its value is not being changed. So after exiting the function the pointer to the head node will still have its original value.
The function can be defined the following way
void deleteList( Node **list )
{
while ( *list != NULL )
{
Node *ptr = *list;
*list = ( *list )->next;
free( ptr->data );
free( ptr );
}
}
The function pop does not check whether the passed pointer to the head node of the list is equal to NULL.
Node *pop(Node **list) {
Node *newptr = (*list)->next;
deleteNode(list);
return newptr;
}
Also it returns a pointer to the next node in the list that becomes the current head node. But the returned pointer is not used in main
pop(list);
Pay attention to that the expression 1 * sizeof( char ) does not make a sense in the expression used as an initializer in this declaration
temp->data = (char*)malloc(len + 1*sizeof(char));
Either write
temp->data = (char*)malloc(len + 1);
or like
temp->data = (char*)malloc( ( len + 1 )*sizeof(char));

How to split a linked-list into two lists

I'm writing a code to split a circular linked-list to two linked lists with equal number of codes, following is my code:
#include <stdio.h>
#include <stdlib.h>
typedef struct node *ptr;
struct node {
int element;
ptr prev;
ptr next;
};
typedef ptr list;
typedef ptr position;
int main() {
list L=malloc(sizeof(struct node));
list first=malloc(sizeof(struct node));
list second=malloc(sizeof(struct node));
splitlist(L,first,second);
return 0;
}
void splitlist(list L, list first,list second) {
position p,temp;
p=malloc(sizeof(struct node));
temp=malloc(sizeof(struct node));
p=L;
int count=0;
while ((p)->next != L) {
count++;
}
int c=count;
while (c!=(count/2)-1) {
p=(p)->next;
temp=(p)->next;
}
first=L;
(p)->next=NULL;
second=temp;
c=count;
while (c!=(count/2)-1) {
temp=(temp)->next;
}
(temp)->next=NULL;
}
When compiling my code it gives no errors but I'm not sure if it's working properly.
In order to get more readable and maintainable code, the first step to improve the code could be to create functions which help manipulating lists. Candidate functions are:
ListInitialize()
ListPushFront()
ListPushBack()
ListPopFront()
ListPopBack()
ListGetFirstNode()
ListGetNextNode()
ListGetFront()
ListGetBack()
ListEmpty()
...
With a proper set of arguments and return values of course.
Then you can write your splitlist function using those basic list operation functions and your code will be easier to read and to reason about.
Also, in order to handle an empty list, you should have an extra list type which is not just a pointer to a node.
typedef struct Node_tag { int value; struct Node_tag *next; struct Node_tag *prev } Node, *NodePtr;
typedef struct IntList_tag { NodePtr front; NodePtr back; } IntList;
// Creates an empty list.
void ListInitialize( IntList *pList ) { pList->front = NULL; pList->back = NULL; }
void ListPushFront( IntList *pList, int value )
{ NodePtr newNode = malloc(sizeof(Node));
if(NULL != newNode )
{ newNode->next = pList->front;
newNode->prev = NULL; newNode->value = value;
pList->front = newNode;
if( pList->back == NULL ) pList->back = newNode; // first element...
}
}
// ...
Eventually, using those functions, you can write splitlist() function in a concise and noise-free way:
void splitlist( IntList * source, IntList *target1, IntList *target2 )
{
IntList * currentTarget = target1;
for( NodePtr currentNode = ListGetFirstNode(source); currentNode != NULL; currentNode = ListGetNextNode(currentNode) )
{
ListPushBack(currentTarget, currentNode->value );
if(currentTarget == target1 ) currentTarget = target2;
else currentTarget = target1;
}
}
It might appear that it is much work to create all those other list functions if all you want is splitlist. But in real world applications you will most likely want all those other functions as well (or you have them already). Only in homework situations, this looks a bit funny.
Example code. Using typedef for node to be compatible with Microsoft C compilers (C89). Note sometimes the pointer to a circular list is a pointer to the last node of the circular list, (which contains a pointer to the first node of the circular list), allowing for faster appends. This example assumes list pointers are pointers to first nodes, but could be modified to assume list pointers are to last nodes.
#include <stdlib.h>
typedef struct _node{
struct _node *next;
int data;
}node;
node * splitlist(node * psrc, node ** ppdst1, node ** ppdst2)
{
node *ps = psrc;
node ** ppd1 = ppdst1;
node ** ppd2 = ppdst2;
*ppd1 = *ppd2 = NULL;
if(ps == NULL)
return NULL;
while(1){
*ppd1 = ps;
ps = *(ppd1 = &(ps->next));
if(ps == psrc)
break;
*ppd2 = ps;
ps = *(ppd2 = &(ps->next));
if(ps == psrc)
break;
}
*ppd1 = *ppdst1;
*ppd2 = *ppdst2;
return NULL;
}
main()
{
node a[8] = {{&a[1],0},{&a[2],1},{&a[3],2},{&a[4],3},
{&a[5],4},{&a[6],5},{&a[7],6},{&a[0],7}};
node *pa = &a[0];
node *pb = NULL;
node *pc = NULL;
pa = splitlist(pa, &pb, &pc);
return 0;
}

Implementing a list C

I have the code with the structure of a list and the code that implements it.
Structure,entry_t is the type of data on the list:
#ifndef _list_private_h
#define _list_private_h
typedef struct list_t{
struct node_t *head;
int size;
};
typedef struct node_t{
struct entry_t *element;
struct node_t *next;
}node_t;
#endif
Code:
struct list_t *list_create(){
struct list_t *list = (struct list_t*) malloc(sizeof(struct list_t));
list->head=NULL;
list->size=0;
return list;
}
int list_destroy(struct list_t *list){
node_t *no = list->head;
while(no!=NULL){
node_t *aux=no;
entry_destroy(no->element);
no=no->next;
free(aux);
list->size=(list->size)-1;
}
free(list);
return 0;
}
int list_add(struct list_t *list, struct entry_t *entry){
node_t *no = list->head;
if(no==NULL){
list->head=(node_t*) malloc(sizeof(node_t));
list->head->element=entry_dup(entry);
list->size=list->size+1;
return 0;
}
else{
while(no!=NULL){
no=no->next;
}
no=(node_t*) malloc(sizeof(node_t));
no->element=entry_dup(entry);
list->size=list->size+1;
return 0;
}
return -1;
}
struct entry_t *list_get(struct list_t *list, char *key){
node_t *no = list->head;
while(no!=NULL){
if(strcmp(no->element->key,key)==0){
return no->element;
}
no=no->next;
}
return NULL;
}
When I run these tests it doesn't add the element to the list:
int testEmptyList() {
struct list_t *list = list_create();
int result = list != NULL && list_size(list) == 0;
list_destroy(list);
printf("Test empty list: %s\n",result?"pass":"not pass");
return result;
}
int testAddHead() {
int result;
struct list_t *list = list_create();
struct entry_t *entry = entry_create(strdup("abc"),data_create(5));
memcpy(entry->value->data,"abc1",5);
list_add(list,entry);
result = list_get(list,"abc") == entry &&
list_size(list) == 1;
list_destroy(list);
printf("Module list -> test add first: %s\n",result?"pass":"not pass");
return result;
}
So, what I want is put this code adding elements to the list. Thanks.
Several issues:
You are destroying the list via list_destroy which can call entry_destroy on the entry added to the list before calling list_get which returns a pointer to (not a copy of) an entry.
In list_add you call malloc to allocate space for a new node, however you don't set its next element to NULL. Since malloc does not guarantee that the memory allocated is wiped, the list may never end with a node that has its next element set to NULL resulting in spurious results.
Your else branch in list_add guarantees no will be NULL (or the program will have crashed from a segfault given earlier problems.) You probably want to terminate when no->next is NULL instead of when no is NULL. Also, this branch needs to assign the next element to NULL explicitly.
Try this:
int list_add(struct list_t *list, struct entry_t *entry){
node_t *no = list->head;
if(no==NULL){
list->head=(node_t*) malloc(sizeof(node_t));
list->head->element=entry_dup(entry);
list->size=list->size+1;
return 0;
}
else{
while(no->next!=NULL){
no=no->next;
}
no->next=(node_t*) malloc(sizeof(node_t));
no->next->element=entry_dup(entry);
no->next->next = NULL;
list->size=list->size+1;
return 0;
}
return -1;
}
The problem is that the previous node needs to know the address of the next one, via the pointer next. In your case, no->next will be equal to NULL (after the loop), so it's the last node. You never assign the next pointer of the last node to the new node, so it will be lost.

Resources