Changing the variable assigned as a pointer to the parameter in C - c

Firstly I'm using C language. I want a variable that I refer to as a parameter to change outside the function when it is changed inside the function. But the problem is that the variable is of type linked list and I want to add to the linked list.
So what I'm trying to do is add the linked list outside the function with the save function.
Is this possible? If possible how can I do it? Sorry for the English, I used a translator.
My codes:
#include <stdio.h>
#include <stdlib.h>
typedef struct NS
{
char * name;
char * value;
struct NS * next;
} n_t;
void save(char * name, char * value, n_t ** item)
{
n_t * link = malloc(sizeof(struct NS));
link->name = name;
link->value = value;
link->next = NULL;
n_t ** pointer = NULL;
if (*item == NULL)
*item = link;
pointer = &(*item);
while ((*pointer)->next != NULL)
*pointer = (*pointer)->next;
(*pointer)->next = link;
}
int main()
{
n_t * mem = NULL;
save("hello", "val123", &mem);
printf("-> %s\n", mem->value);
save("abc", "hello", &mem);
printf("-> %s\n", mem->value);
printf("-> %s\n", mem->next->value);
return 0;
}
The output for the first arrow (->) should be "val123"
The result that should also appear in the second arrow output is "val123"
The result that should appear in the third arrow output is "hello"

All this part of the function
n_t ** pointer = NULL;
if (*item == NULL)
*item = link;
pointer = &(*item);
while ((*pointer)->next != NULL)
*pointer = (*pointer)->next;
(*pointer)->next = link;
does not make sense. For example in the while loop
while ((*pointer)->next != NULL)
*pointer = (*pointer)->next;
there is rewritten the value of the passed original pointer to the function by reference,
The function can look the following way
int save( n_t **item, char *name, char *value )
{
n_t * link = malloc( sizeof( struct NS ) );
int success = link != NULL;
if ( success )
{
link->name = name;
link->value = value;
link->next = NULL;
while ( *item != NULL ) item = &( *item )->next;
*item = link;
}
return success;
}
In general you need to dynamically allocated memory to store copies of the strings name and value in the nodes of the list.
The body of the function main can look like
n_t * mem = NULL;
save( &mem, "hello", "val123" );
printf("-> %s\n", mem->value);
save( &mem, "abc", "hello" );
printf("-> %s\n", mem->next->value );
printf("-> %s\n", mem->next->next->value);

You're really only missing a single return when the list is empty still:
if (*item == NULL) {
*item = link;
return;
}

Related

Adding items to a linked list

I'd like to add an element to a list of element. My list is a struct containing a double, an integer and a pointer to the next element. Could someone tell me how to do the Add function please
#include <stdio.h>
#include <stdlib.h>
typedef struct Liste Liste;
struct Liste{
double c;
int n;
Liste* next; // pointe sur l'élément suivant
};
void Add(Liste array, Liste item) {
Liste* last = array.next;
while (last != NULL) {
last = last->next;
}
array.next = &item;
printf("%p\n", array.next);
}
int main(){
Liste array = {12.4, 4, NULL};
printf("%f\n", array.c);
Liste item = {15.4, 7, NULL};
Add(array, item);
printf("%p\n", array.next);
return 0;
}
Pass-by-value
In Add, C makes a copy of all the function parameters; their scope is the function itself. When one returns, the function parameters are popped from the stack and there is no way to get them back, as you have seen. The way to mutate structures is to pass a pointer to the structure, then modify that pointer using the structure pointer dereference operator, (arrow ->.)
Design
The reason one would use a linked-list is it is very cheap to reorder it, but the head of your linked-list is fixed, so you can't change it. You might change this by delineating the container, the list itself, from the contents. This is similar to using a double-pointer, but I think less confusing.
struct Noeud {
double c;
int n;
struct Noeud* next; // pointe sur l'élément suivant
};
struct Liste {
struct Noeud *tete; // singly-linked-list est defini par un pointer seul
};
Then you can add, (I've included assert.h.)
/* `O(n)` */
static void AddQueue(struct Liste *liste, struct Noeud *item) {
assert(liste && item && item->next == NULL);
struct Noeud* last = liste->tete;
if(last == NULL) { // case spécieux
liste->tete = item;
} else {
while (last->next != NULL) {
last = last->next;
}
last->next = item;
}
}
However, it's much simpler and asymptotically faster to add at the beginning of the list.
Pointerstructures like a linked list are powerful tools with a wide rage of application.
But first you have to understand pointers.
A pointer is a datastructure which contains the address of a datastructure.
Whenever you call a function the arguments of it are copied (pushed) to the stack.
If the arguments require a lot of storage space you use a pointer instead.
the code below uses pointers to create a linked list
#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"
typedef struct List List;
struct List{
double c;
int n;
List *next;
};
void AddItemEnd( List *RootItem, List *Item )
{
List *Last = RootItem;
while( Last->next != NULL )
{
Last = Last->next;
}
Last->next = Item;
}
void AddItemAtPos( List *RootItem, List *Item, unsigned int Pos )
{
if( Pos == 0 )
{
Item->next = RootItem;
}
else
{
List *TempItem = RootItem;
for( unsigned int i = 1; i < Pos && TempItem->next != NULL; ++i )
{
TempItem = TempItem->next;
}
Item->next = TempItem->next;
TempItem->next = Item;
}
}
void RemoveItemAtPos( List *RootItem, unsigned int Pos )
{
if( Pos == 0 )
{
free( (void*) RootItem );
}
else
{
List *TempItem = RootItem;
for( unsigned int i = 1; i < Pos && TempItem->next != NULL; ++i )
{
TempItem = TempItem->next;
}
if( TempItem->next == NULL )
{
return;
}else if( TempItem->next->next != NULL )
{
List *ItemToDelete = TempItem->next;
TempItem->next = TempItem->next->next;
free( (void*) ItemToDelete );
}else
{
free( (void*) TempItem->next );
TempItem->next =NULL;
}
}
}
int main(void) {
List *RootItem = malloc( sizeof( List ));
RootItem->c = 12.4;
RootItem->n = 4;
RootItem->next = NULL;
List *Item1 = malloc( sizeof(List ));
Item1->c = 15.4;
Item1->n = 7;
Item1->next = NULL ;
AddItemEnd( RootItem, Item1 );
List *IterationItem;
printf( "List created with AddItemEnd()\n\n" );
for( IterationItem = RootItem; IterationItem != NULL; IterationItem = IterationItem->next )
{
printf( "c: %lf\nn: %d\n\n", IterationItem->c, IterationItem->n );
}
List *item2 = malloc( sizeof( List ));
item2->c = 23.4;
item2->n = 1846;
item2->next = NULL ;
AddItemAtPos( RootItem, item2, 1 );
printf( "\n\nList extended with AddItemAtPos()\n\n");
for( IterationItem = RootItem; IterationItem != NULL; IterationItem = IterationItem->next )
{
printf( "c: %lf\nn: %d\n\n", IterationItem->c, IterationItem->n );
}
RemoveItemAtPos(RootItem, 1 );
printf( "\n\nList after RemoveItemAtPos()\n\n");
for( IterationItem = RootItem; IterationItem != NULL; IterationItem = IterationItem->next )
{
printf( "c: %lf\nn: %d\n\n", IterationItem->c, IterationItem->n );
}
free( (void*) RootItem );
free( (void*) item2 );
return 0;
}
The key elements when dealing with lists is pointers
and using memory allocation.
If we disregard your add function and just do a simple
example you will probably get the geist of it.
First allocate you starting list like this
Liste* array = malloc(sizeof(Liste));
Now you have one uninitialized block of memory
that array points to. You then need to initialize
it.
array->c = 12.4;
array->n = 4;
array->next = NULL;
in order to add a new entry to your list you
need to again allocate memory for the next node and
initialize it plus set the previous node next pointer
to point to it i.e. array->next.
Liste* item = malloc(sizeof(Liste));
item->c = 15.4;
item->n = 7;
item->next = NULL;
array->next = item;
now you have a list of two elements where array points
to the first
printing your short list
Liste* p = array;
while (p != NULL)
{
printf("%lf %d %p\n", p->c, p->n, p->next);
p = p->next;
}
So your Add functions does not allocate memory and copies
the parameters so that is not going to work.
Your Add function should have a pointer either to either the first or last item in your list e.g.
void Add(Liste* start, double c, int n)
Then you do as I showed you above and create a new node and assign the values
If you want to be able to pass an empty list to Add then you need to do differently, since start is copied it cannot be changed, you need to pass the address of the pointer
void Add(List** start, double c, int n)
{
Liste* node = malloc(sizeof(Liste));
...
(* put node in the list *)
if (*start == NULL)
{
*start = node; // first
}
else
{
(* find last node, see print loop *)
(* once you have last item, set it to point to node)
}
...
}
int main()
{
Liste* start = NULL;
Add(&start, 12.4, 4);
Add(&start, 15.4, 7);
...

Linked list doesn't function properly

I'm attempting to extract text from a text file using linked lists. My code so far:
#include <stdlib.h>
#include <string.h>
#define N 20
struct node
{
char * str;
struct node * node;
};
typedef struct node Node;
typedef Node * WordList;
typedef Node * Node_ptr;
void addtext(WordList * lst_ptr);
void report(WordList words);
Node * createnode(char *str);
int main(void)
{
WordList words = NULL;
addtext(&words);
report(words);
return 0;
}
Node * createnode(char *str)
{
Node_ptr newnode_ptr ;
newnode_ptr = malloc(sizeof (Node));
newnode_ptr -> str = NULL;
newnode_ptr -> node = NULL;
return newnode_ptr;
}
void addtext(WordList * lst_ptr)
{
FILE *fileinput;
char word[N];
char *pnt;
if ((fileinput = fopen("test.txt", "rt"))==NULL)
{
printf("Cannot open file. Please check if the file is in the right directory and you are giving the correct name.");
exit(EXIT_FAILURE);
}
while (fscanf(fileinput, "%19s", word) == 1)
{
Node * newnode = createnode(word);
if (*lst_ptr==NULL)
{
newnode->node = *lst_ptr;
*lst_ptr = newnode;
}
else
{
Node *iter;
for (iter = *lst_ptr; iter -> node != NULL; iter = iter -> node);
iter -> node = newnode;
}
pnt = strtok (word," ");
while (pnt != NULL)
{
newnode->str = pnt;
pnt = strtok (NULL, " ");
}
}
fclose(fileinput);
return ;
}
void report(WordList words)
{
if (words == NULL)
{
printf("\n") ;
return ;
}
printf("%s\n",words -> str);
report(words -> node);
return;
}
The text file I've made contains the following:
Hello
Bye
But the output of my program is:
Bye
(random characters)
Meaning that it prints the last word. My guess is that when I call report it point to the last element of the list and keeps going from there for as many times as the amount of words in the text file.
I also tried a different approach on the report function:
void report(WordList words)
{
Node * iter;
for (iter = words; iter!=NULL; iter = iter->node)
{
printf("%s\n", iter->str);
}
return;
}
But now it prints \n until the loop ends. Am I missing something simple or am I making a grave mistake?
The strtok() modifies the original string and returns pointers to elements of the original string. In this case the original string is stored in a local array char word[N];. The local array will vanish on returning the function addtext and refering the array from the function report after that is illegal.
To avoid this trouble, you should allocate some new regions and copy the string there. It can be done like this:
pnt = strtok (word," ");
while (pnt != NULL)
{
newnode->str = malloc(strlen(pnt) + 1); /* allocate a region (+1 for terminating null-character) */
if (newnode->str == NULL) /* check if allocation succeeded */
{
/* handle allocation failure */
exit(1);
}
strcpy(newnode->str, pnt); /* copy the string */
pnt = strtok (NULL, " ");
}
Simplified:
newnode->str = strdup(pnt);

Function to insert node in linked list

I'm reading in words from a dictionary and then adding them to linked lists in a hash table. This works fine when I try inserting the nodes for each word within the while loop.
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
FILE *dict = fopen(dictionary, "r");
if (dict == NULL)
{
return false;
}
// Set all next pointers to NULL in hash table
for (int i = 0; i < N; i++)
{
table[i] = NULL;
}
char word[LENGTH + 1];
while(fscanf(dict, "%s", word) != EOF)
{
// Get key from hash function
unsigned int key = hash(word);
node *pNode = getNode(word);
if (table[key] != NULL)
{
pNode->next = table[key];
}
table[key] = pNode;
words++;
}
fclose(dict);
return true;
}
I've tried refactoring this to a function insertNode with the exact same code but it doesn't work and the nodes seem to get lost and cause a memory leak. I assume it has something to do with how the arguments are passed into the function but as head is a pointer I would've thought it would work fine.
void insertNode(node *head, const char *key)
{
// Create node
node *pNode = getNode(key);
// Insert node into linked list
if (head != NULL)
{
// Make new node point to first item in linked list (a.k.a head)
pNode->next = head;
}
// Now point head to new node
head = pNode;
}
so the while loop within load would just call the function (which is defined before)
char word[LENGTH + 1];
while(fscanf(dict, "%s", word) != EOF)
{
// Get key from hash function
unsigned int key = hash(word);
// Add value to Hash table with head of linked list
insertNode(table[key], word);
words++;
}
As the 'head' variable is a pointer, you can just pass the value of 'head' by this pointer not the pointer itself, and in this case you try to override the local pointer inside the function.
Well look at this example to assign/change value to the pointer:
#include <stdio.h>
class A {
public:
int x;
};
// pass pointer by copy
void initialize(A* obj) {
obj = new A(); // obj not null here
obj->x = 2;
printf("x: %d\n", obj->x);
}
int main() {
A *a = nullptr;
initialize(a);
// a is still null here (pointer passed by copy)
printf("x: %d\n", a->x); // seg fault here, read on null
return 0;
}
The following code as you can see is incorrect. To fix this example you have to change the function prototype, and pass the pointer by pointer so it should lool like this:
#include <stdio.h>
class A {
public:
int x;
};
// pass pointer by pointer
void initialize(A** obj) {
*obj = new A(); // obj not null here
(*obj)->x = 2;
printf("x: %d\n", (*obj)->x);
}
int main() {
A *a = nullptr;
initialize(&a); // get the pointer address
// a is valid object here
printf("x: %d\n", a->x); // no error, x == 2
return 0;
}
So in your case it should be:
insertNode(&table[key], word);
and
void insertNode(node **head, const char *key)
{
// Create node
node *pNode = getNode(key);
// Insert node into linked list
if (*head != NULL)
{
// Make new node point to first item in linked list (a.k.a head)
pNode->next = *head;
}
// Now point head to new node
*head = pNode;
}

Enqueue/Dequeue two linked lists simultaneously in C

I have two linked lists that I track using inputFront, inputRear, outputFront & outputRear. My problem arises when I enqueue the data from input into output. Printf prints only the first item from the input. Ex: my input is "a b c", it will only return "a". I'd appreciate the help. Thanks.
struct linked_list{
char *operand;
struct linked_list *next;
};
void enqueue(struct linked_list** queueFront, struct linked_list** queueRear,
char* token);
char* dequeue(struct linked_list** queueFront, struct linked_list** queueRear);
int main(int argc, char *argv[]){
struct linked_list *inputFront = NULL, *inputRear = NULL;
struct linked_list *outputFront = NULL, *outputRear = NULL;
for (int i = 1; i < argc; i++)
enqueue(&inputFront, &inputRear, argv[i]);
for (struct linked_list *p = inputFront; p != NULL; p = p->next)
enqueue(&outputFront, &outputRear, dequeue(&inputFront, &inputRear));
for (struct linked_list *p = outputFront; p != NULL; p = p->next)
printf("%s \n", dequeue(&outputFront, &outputRear));
}
void enqueue(struct linked_list** queueFront, struct linked_list** queueRear,
char* token){
struct linked_list* newNode = (struct linked_list*) malloc(
sizeof(struct linked_list));
newNode->operand = token;
newNode->next = NULL;
if (*queueRear == NULL && *queueFront == NULL){
*queueFront = *queueRear = newNode;
return;
}
(*queueRear)->next = newNode;
*queueRear = newNode;
}
char* dequeue(struct linked_list** queueFront, struct linked_list** queueRear){
if (*queueFront != NULL){
struct linked_list *remv = *queueFront;
char *remOperand = (*queueFront)->operand;
*queueFront = remv->next;
if (remv == (*queueRear))
*queueRear = NULL;
free(remv);
return remOperand;
}
else
return NULL;
}
There are 2 issues with your code. See.
for (struct linked_list *p = inputFront; p != NULL; p = p->next)
enqueue(&outputFront, &outputRear, dequeue(&inputFront, &inputRear));
You are using a pointer p to run through the list, but this pointer is not used inside the loop. The inputFront in the statement must be replaced by this pointer p. This will allow it to run through every element of the loop.
In the dequeue function, you are moving the pointer inputFront (now p). So, if you do p = p-> next in the loop, it will be done twice. This has to be removed.
Fixed code is
for (struct linked_list *p = inputFront; p != NULL; )
enqueue(&outputFront, &outputRear, dequeue(&p, &inputRear));
for (struct linked_list *p = outputFront; p != NULL; )
printf("%s \n", dequeue(&p, &outputRear));
The problem seems to be in your print loop. You let p advance until the end of the list, and you let dequeue retrieve the next (first) item in the list. But dequeue also removes the front from the list. Following the return, the for-loop now also advances p, whose next member has jus been advanced already. Given input a b c, I would expect the output now to be a c. If you take a debugger, you can get some more clues.

C - Doubly Linked List always null

I am writting a program on doubly linked list with the following data structure:
typedef struct telephoneBookNode {
int id;
char name[NAME_LENGTH];
char telephone[TELEPHONE_LENGTH];
struct telephoneBookNode * previousNode;
struct telephoneBookNode * nextNode;
} TelephoneBookNode;
typedef struct telephoneBookList {
TelephoneBookNode * head;
TelephoneBookNode * tail;
TelephoneBookNode * current;
} TelephoneBookList;
In the following function, I read data from a text file to the linked list, the file content look like this:
/*100, Alice, 0411112222
101, Bob, 0411112222
102, Ali, 0411112223*/
TelephoneBookList * commandLoad(char* fileName) {
TelephoneBookList *(*createList)(TelephoneBookNode*, char[]) = createTelephoneBookList;
char entry[100], *temp1, *temp2;
TelephoneBookList* aList = NULL;
TelephoneBookNode* aNode = NULL;
FILE* telephoneListFile = NULL;
int countEntry = 0;
Boolean check;
telephoneListFile = fopen(fileName, "r");
if (!telephoneListFile)
return NULL;
else {
while (fgets(entry, 100, telephoneListFile)) {
temp2 = strcpy(temp2, entry);
temp1 = strtok(entry, "\n");
check = addressBookEntryCheck(temp1);
if (!check)
return NULL;
else
//here I pass aNode pointer to the below function
aList = (*createList)(aNode, temp2);
}
fclose(telephoneListFile);
printf("printed"); //This line is reached when program complied
return aList;
}
}
This is the function to create the list, problem may be here: it doesnot add new node to the list, it just replaces the first node with the new one. Finally, the linked list only has 1 record which was the last one in text file. How can I fix the code? Thank you!
TelephoneBookList * createTelephoneBookList(TelephoneBookNode* node, char entry[]) {
TelephoneBookList* aList = malloc(sizeof *aList);
TelephoneBookNode* aNode = (TelephoneBookNode*) malloc(sizeof *aNode);
char *tokens;
tokens = strtok(entry, ", ");
aNode->id = atoi(tokens);
tokens = strtok(NULL, ", ");
strcpy(aNode->name, tokens);
tokens = strtok(NULL, ", ");
strcpy(aNode->telephone, tokens); //Just assigning values to a node
//program always go to this block, means `node` is always null
if (node == NULL) {
aNode->nextNode = NULL;
aNode->previousNode = NULL;
node = aNode;
aList->current = node;
aList->head = node;
aList->tail = node;
}
else { //This block is not reached
while (node->nextNode)
node = node->nextNode;
node->nextNode = aNode;
aNode->previousNode = node;
aList->tail = node->nextNode;
}
return aList;
}
This is the function to check entry:
Boolean addressBookEntryCheck(char entry[]) {
char *tokens;
tokens = strtok(entry, ", ");
if(!tokens || strlen(tokens) < 1 || strlen(tokens) > 3)
return FALSE;
else {
if (!isNumber(tokens))
return FALSE;
else {
tokens = strtok(NULL, ", ");
if (!tokens)
return FALSE;
else
{
tokens = strtok(NULL, ", ");
if (!tokens)
return FALSE;
else if (!isNumber(tokens) || strlen(tokens) != 10)
return FALSE;
else
return TRUE;
}
}
}
}
Every time you call
createTelephoneBookList
you create a new list
TelephoneBookList* aList = malloc(sizeof *aList);
You also copy to an uninitialized pointer
temp2 = strcpy(temp2, entry);
I would suggest you create one function to create the list header, one function to add new items e.g.
aList = createList()
while (fgets(entry,sizeof(entry),fp)!=NULL)
{
if (!addEntry(aList,entry))
{
fprintf(stderr, "failed additem item %s\n", entry);
}
}
...
In addEntry parse the string
int id = 0;
char name[NAME_LENGTH];
char telephone[TELEPHONE_LENGTH];
p = strtok(entry, ","); // id
if (p != NULL)
{
id = atoi(p);
p = strtok(NULL, ","); // name, store to temporary string
if (p != NULL )
{
strcpy(name,p);
p = strtok(NULL, ","); // telephone number, store to temporary string
if ( p != NULL )
{
strcpy(telephone,p);
// here you can allocate the new node
}
}
}
// disclaimer omitted checks for length etc which any good program should have. also make sure you have room for \0
if any of the strtok above fail return 0 otherwise allocate a new entry
TelephoneBookNode* aNode = malloc(sizeof(TelephoneBookNode));
aNode->id = id;
strcpy(aNode->name, name);
strcpy(aNode->telephone, telephone);
Then add to your aList
//program always go to this block, means `node` is always null
if (node == NULL) {
....
This is because the caller of the function passed aNode and it never changes within that loop. So it will always pass same value of aNode which is NULL.
I haven't looked at the logic of your code in details, but I think you may want to pass aList->head or rather you already pass aList so just use that.

Resources