Changing value of Linked List without flexiblility in parameters - c

I have an assignment. I was provided with a function declaration that I cannot modify.
The function declaration is void Insert (Item x, int p, List *L); where I am supposed to change the values of the linked list struct, L.
Now, the code that invokes that method in my main function is
struct List *L = malloc(sizeof(List));
Insert(x,p,L);
How would I change my code so I can pass the address of the struct List instead of making another copy of it?
Like I said, I cannot change the function declaration at all.
/*********************************************************************
* FUNCTION NAME: Insert
* PURPOSE: Inserts an Item in a List.
* ARGUMENTS: . The Item to be inserted (Item)
* . The position in the List
* where the Item should be inserted in (int)
* . The address of the List (List *L)
* REQUIRES (preconditions):
* . The position should be a nonnegative integer
* not greater than the size of the List.
* . The List should not be full.
* ENSURES: . Empty will return false (0).
* . Size will return the first integer greater
* than the size of the List before the call.
* . Peek in the same position will find
* the Item that was inserted.
*********************************************************************/
extern void Insert (Item X, int position, List *L);
What I tried that didn't work was
head->next = L; //changing the next item in list to L
L = head; //changing the address of L so it remains the head of the list

I think this will work:
void Insert (Item x, int p, List *L) {
struct List newnode, last = *L;
newnode = (struct List)malloc(sizeof(struct List));
newnode->item = x;
newnode->next = NULL;
if (last == NULL){
if (p == 0) { *L = newnode; }//first node
else { printf("List is empty and index %d does not exist", p); }
} else if (p == 0) {
newnode->next = *L;
*L = newnode;
}
else{
int counter = 0;
while (1) {
if (counter == p) {
newnode->next = last->next;
last->next = newnode;
break;
}
last = last->next;
counter++;
if (last->next == NULL && counter != p){ break; }
}
}
}

You might want to review the differences (or similarities) between addresses, pointers in C because the answer to your question is that you are already passing the address of your struct List.
I will try and explain a bit. malloc() returns a memory address, AKA a pointer, which is denoted by the * symbol in struct List *L. When I read this line in my head, I would say "L contains a pointer to a struct List object" or "L contains the memory address of a struct List object".
So in that case, when you write Insert(L, x) you are already passing a pointer to your struct List object. And no other copy is being made. So any actions you perform inside of your Insert() function will be acting on the original list.
The only exception to this rule is if you try to reassign L in your insert method, like this:
void Insert(struct List* L, int x) {
L = NULL; // or L = malloc(sizeof(struct List));
}
This will not do what you might expect, but the reason for that is more complicated and something you probably don't need to know for the time being.

There are irregularities with your function declarations.
Your code suggests that you have a function with the following signature :
void Insert (struct List *L, int x);
The header file that you have linked uses a function insert with this signature :
extern void Insert (Item X, int position, List *L);
Note the additional parameter Item X.
There isn't much to go on since you haven't showed us much of your code, but I would suggest taking a look at your missing parameter.

First of all:
How would I change my code so I can pass the address of the struct List instead of making another copy of it?
You are already doing it: void Insert (Item x, int p, List *L); takes pointer to List structure, not structure itself, so you are not copying it.
Secondly:
What I tried that didn't work was head->next = L; //changing the next item in list to L
L = head; //changing the address of L so it remains the head of the list
It won't work. Inside Insert function L is value of pointer to List. So if you change this value inside the function it won't be change outside, as value of L is passed by copy. Only changes to *L will be seen, as they will not change value of L itself, but value of what L is pointing to.
I can assure you, that no L = is needed inside Insert function to complete this task.
Moreover take good look at prerequisites - they should discribe situations, which you do not need to trouble yourself with (probably, I don't know person which created this task, but I would understand it this way) - meaning, if user doesn't comply to them, it's users own fault, anything bad can happen.

Related

passing linked list to function by value

EDIT: my struct is of the form:
struct node{
int key;
struct node* next;
}
typedef struct node* LIST;
I'm writing a function, called print_list_iteratively, which takes a singly linked list and prints its elements in reverse order iteratively. Let me put my 3 functions below and then explain what's my problem with them?
LIST reverse_list(LIST L) {
LIST current = L, prev = NULL, subsequent = NULL;
while (current != NULL) {
// first we need to get the next element to not lose the link.
subsequent = current->next;
// now reverse the pointer
current->next = prev;
// we need to update our prev to create link between the currrent and next element.
// in short, we need to get the address of current node
prev = current;
// finally update the current
current = subsequent;
}
return (L = prev);
}
The above code reverses the list L, and returns the new head of the list L.
void print_list(LIST L) {
while (L) {
printf("%d ", L->key);
L = L->next;
}
}
print_list prints the elements of the list in the order they've been connected.
I've all needed to write the print_list_iteratively function I mentioned at the beginning.
void print_list_iteratively(LIST l) {
// first reverse the list, then call print_list. that's all.
printf("address of function parameter is %p\n", l);
l = reverse_list(l);
print_list(l);
}
I've no problem with these functions; they work as expected, but the notion of pass by value and pass by reference confused me, when I tried to understand how the function takes list as a parameter and returns it. Let me simply write a main for testing these and we'll see how things get mixed.
int main(void) {
l = insert(l, 5);
l = insert(l, 10);
l = insert(l, 15);
l = insert(l, 45);
print_list(l);
print_list_iteratively(l);
print_list(l);
return 0;
}
when first print_list function is called, it prints 5->10->15->45. Next print_list_iteratively is called on list l, which in its turn calls reverse_list and print_list in the body of the function. The print_list inside print_list_iteratively give reversed list 45->15->10->5. Eventually, print_list inside main prints 5.
I want to know why has second print_list just printed 5? I've send list to print_list_iteratively pass by value, not pass by referce, despite this it's changed my original list insinde main function. Can somebody explain me how the lists in this example are passed to functions and how they're returned from them? I appreciate your answers.
The problem is that you pass the head pointer by value. This lets your program to modify your linked list, but the head pointer remains the same.
You should pass the head pointer to the function by reference or a pointer which points to your pointer. (node*& or node**)
Keep in mind, if you pass it by pointer, you need to dereference it, when it is needed.

How to correctly iterate over linked list on function call

Considering this linked list definition:
typedef struct lligada{
int value;
struct lligada *prox;
}*LInt;
void newInteger(LInt l, int value){
LInt p = l;
LInt node = (LInt)malloc(sizeof(struct lligada));
node->value = value;
node->prox = NULL;
while (p->prox) p = p->prox;
p->prox = node;
}
Is that p auxiliary pointer necessary / good practice? Or can I iterate through LInt l directly?
You don't need p; since C passes argument by value, l only contains a copy of the value of the original argument, so changes to l have no effect outside of the function. Note that this is different from changing what l points to, which can have an effect outside the function, as would changing what p points to.
In general the function is invalid because the list can be empty and inside the function there is changed local variable l that will be destroyed after exiting the function because function parameters are its local variables. Thus an empty list will not be changed.
Also it is not clear why you are trying to add a new node to the tail of a singly-sided list.
In any case the function can look for example like this
void newInteger( LInt *l, int value )
{
LInt node = malloc( sizeof( *LInt ) );
if ( node != NULL )
{
node->value = value;
node->prox = NULL;
while ( *l != NULL ) l = &( *l )->prox;
*l = node;
}
}
You can iterate through the local variable l. It is already a copy (most likely allocated on the stack) of the pointer that was sent to the function. Changing it won't affect the original pointer.
To answer the questions:
It's not necessary.
I can't see a reason for it to be good practice.
PS - Maybe you want some error checking for null pointers on that function.

linked list insert function - passing a list by pointer

Im trying to create linked-list insert function that takes a list (or more correctly a pointer to it) then inserts the value to the end of the list.
void ll_insert(struct ll **l, int n){
struct ll *temp=NULL;
while ( (*l) != NULL){
temp= (*l);
(*l) = (*l)->next;
}
(*l)= (struct ll*)malloc(sizeof(struct ll));
(*l)->n=n;
(*l)->next=NULL;
if (temp) temp->next= (*l);
}
int main(void){
struct ll *l=NULL;
ll_insert(&l, 1);
printf("%d ", l->n);
ll_insert(&l, 1);
ll_insert(&l, 2);
ll_insert(&l, 3);
printf("%d ", l->n);
}
The output after running the code above is 1 3. This is no surprise, since
(*l) = (*l)->next;
updates the list to point to to the end node, and every time I run insert(...) the list's head is updated to point to the end (if im not wrong). What's the way around this?
You are not using the pointer to pointer correctly: this line in the while loop
(*l) = (*l)->next;
should be
l = &((*l)->next);
If you use it that way, you wouldn't need your temp variable at all.
Since this is C, not C++, it is OK to not cast malloc.
Your function should only change *l if it is inserting into an empty list, as this is the only case where the first element of the list changes. This can be accomplished by using a local variable instead of *l inside of your function for those cases (initialized to *l).
if you don't move the pointer l, then it remains at the head of the list. First assign l to temp, then move temp along the list but leave pointer l alone.

C issue - Can't figure how to assign pointer to beginning of list

I have a simple assignment that the professor wants us to do.
Basically to pull in some numbers from a text file and load into a linked list.
I don't want to get to much into the details but I have a basic question.
He provided us with a function like so:
INTLIST* init_intlist( int n )
{
INTLIST *lst;
lst = (INTLIST *)malloc(sizeof(INTLIST));
lst->datum = n;
lst->next = NULL;
return lst;
}
This function is used to initialize the linked list with the first element. Then he asked us to define a function with this signature:
int insert_intlist( INTLIST *lst, int n )
So I assume he just wants us to add to the linked list so I tried this:
int insert_intlist( INTLIST *lst, int n )
{
INTLIST* lstTemp;
lstTemp = (INTLIST *)malloc(sizeof(INTLIST));
lstTemp->datum = n;
lstTemp->next = lst;
lst = lstTemp;
free(lstTemp);
}
So what my thought process was is that it creates a temporary node, assigns the data value (Datum) and assigns the next pointer to point to where the current pointer is pointing at. Then I reassign the main pointer to this newly created temp node.
That way we have for instance 2 nodes:
[New Temp Node] -> [Prev Initialized Node]
When I step through the code it looks great...
Then back in main I have just a function to print the list:
while (lst!=NULL)
{
printf("The value is:%d", lst->datum);
lst=lst->next;
}
The problem is this only seems to print one digit (namely the first digit that I am reading in from the file, which I think is the last one in the list or at least I thought it was the last one in the list).
But it should keep going through as I have 10 digits in the file. I know the code is very dirty and I will clean it up...here is my entire main function if anyone needs more info:
#include <stdio.h>
#include <stdlib.h>
#include "intlist.h"
int main(int argc, char *argv[])
{
char c; /* Character read from the file. */
FILE* ptr; /* Pointer to the file. FILE is a
structure defined in <stdio.h> */
int index=0;
//INTLIST* aList[10]; //will use later
/* Open the file - no error checking done */
ptr = fopen("1.txt","r");
/* Read one character at a time, checking
for the End of File. EOF is defined
in <stdio.h> as -1 */
if(ptr==NULL) {
printf("Error: can't open file.\n");
/* fclose(file); DON'T PASS A NULL POINTER TO fclose !! */
return 1;
}
//aList[index] = malloc(sizeof(INTLIST)); WE NEED THIS LATER ON....
INTLIST *lst=NULL;
while ((c = fgetc(ptr)) != EOF)
{
if (c != ' ')
{
//make sure it isnt a space
int i = c - '0'; //get the value from the text file
if(c=='\n')
{
// aList[index]=lst;
// index++;
// aList[index] = malloc(sizeof(INTLIST));
while (lst!=NULL)
{
printf("The value is:%d", lst->datum);
lst=lst->next;
}
free(lst);
free(aList[index]);
return 0;
//new line in the file
//create another linked list
}
if (lst==NULL)
lst = init_intlist(i);
else
insert_intlist( lst, i);
}
}
fclose(ptr);
system("PAUSE");
return 0;
}
Here is intlist.h for anyone who may need it:
#ifndef __intlist_h__
#define __intlist_h__
/* each entry in the list contains an int */
typedef struct intlist {
int datum;
struct intlist *next;
} INTLIST;
INTLIST *init_intlist( int n ); /* initializes the intlist with initial datum n */
int insert_intlist( INTLIST *lst, int n ); /* Inserts an int (n) into an intlist from the beginning*/
void list_append(INTLIST *list, void *datum); /* Inserts entry to the end of the list */
INTLIST* list_front(INTLIST *list); /*return the element at the front of the list, and remove it
from the list*/
void list_map( INTLIST *list, void (*f)(void *) ); /*Applies a function to each element of the list */
void list_delete( INTLIST *list ); /* Deletes (and frees) all entries in the list */
#endif
A couple of issues here.
I'll start with a BAD bug:
int insert_intlist( INTLIST *lst, int n )
{
INTLIST* lstTemp;
lstTemp = (INTLIST *)malloc(sizeof(INTLIST));
lstTemp->datum = n;
lstTemp->next = lst;
lst = lstTemp;
free(lstTemp); // <<<<< NO!
}
You are still using that memory, so you can't free it.
Secondly, the proto-type supplied to you for insertion has no way to return a new front of the list, so you can not change the front of the list. This implies that you must add new nodes to the back, rather than to the front as you have done.
Also, the supplied return type of int probably means that he expects out the number of nodes in the list, which is no problem as you're going to have to walk the list to find the back anyway.
Have another go at it, you're not doing badly at all.
Working with code like:
int insert_intlist( INTLIST *lst, int n )
{
INTLIST* lstTemp;
lstTemp = (INTLIST *)malloc(sizeof(INTLIST));
lstTemp->datum = n;
lstTemp->next = lst;
lst = lstTemp;
free(lstTemp);
}
This has a couple of problems. First of all, the free(lstTemp) seems to be freeing the node that you just inserted in the list, which you probably don't want to do.
Second, you're passing the pointer to the list into the function -- which means the function can't modify that pointer, so when you assign to the pointer, you're only changing your local copy of it.
You have two choices: you can either pass in a pointer to that pointer (so you can modify the original pointer) or you can get clever and figure out a way to avoid needing to (but I won't give away the secret right away...)
This line:
lst = lstTemp;
Only changes the value of lst inside the function. It won't propagate back to the copy of the pointer that the caller has.
You can either use a pointer-to-a-pointer, or if you can't change the function signature, insert somewhere other than the head of the list.
Though the typical way of handling this is to not point to the first element in the list - rather, you have some sort of list structure that holds a pointer to the first element, and some other information about the list (say, how many elements it has). Then you pass a pointer to that structure around.
In C, parameters are passed to functions "by value", meaning they are copied when you enter the function and any changes you make to them are not reflected back to the caller. That means that when you modify lst to point to your newly allocated memory it doesn't actually modify the caller's pointer to the list.
EDIT: As dmckee pointed out, you shouldn't free the memory in your insert function as you are still using it. That's definitely a bug, but it's not the one causing your problem.
In C, everything is passed by value. If you want a function to change something, you need to pass its address to the function. Since in int insert_intlist( INTLIST *lst, int n ), you want to change the list head, you need to pass a pointer to it, i.e., the first parameter should be INTLIST **lst (see below too, though). But the function prototype is given and cannot be changed.
What that means is that you can't add a number to the beginning of the list—the caller can't know that you did so. So, you have to traverse the list pointed to by lst, and then add the new node anywhere down the chain. The professor probably wants you to add the node at the end, but he might have asked for some other condition.
With that information, let's look at the comments for the prototypes:
/* Inserts an int (n) into an intlist from the beginning*/
int insert_intlist( INTLIST *lst, int n );
The comment or the prototype is wrong. If your professor has given you this file, insert_intlist() cannot be written to satisfy the comment, since it can't return to the caller the new head. The prototype should be either:
/* Inserts an int (n) into an intlist from the beginning
and returns the new head */
INTLIST *insert_intlist( INTLIST *lst, int n );
Or:
/* Inserts an int (n) into an intlist from the beginning */
int insert_intlist( INTLIST **lst, int n );
(Note the **.)
The header also has:
/*return the element at the front of the list, and remove it from the list*/
INTLIST* list_front(INTLIST *list);
This is correct. Note that you need to modify the list's head in list_front(), so you're returning the new head.
Finally, you don't want to free() anything in insert_intlist(). You want to keep the new node in the list, don't you? Once the caller is done with the list, he will have to call list_delete(), which will traverse the linked list, and free each node.
I Agree with Alok. I Have the same problem/ Professor. I am new to C programming and I have been looking all over the web for forms and C webpages for help. I have came across a source that supports Alok.
I used
INTLIST *list_add(INTLIST **p, int i){
INTLIST *n;
n = (INTLIST *) malloc(sizeof(INTLIST));
if (n == NULL)
return NULL;
n->next = *p; /* the previous element (*p) now becomes the "next" element */
*p = n; /* add new empty element to the front (head) of the list */
n->datum = i;
return p; }
From my main I can pass in
INTLIST *list
list_add(&list, 1);
list_add(&list, 2);
so when i print the list it prints 2 1
The professor suggested this:
INTLIST *mylist[N];
Where N is the number of rows of your
input file. Then mylist[i] is a
pointer to the ith linked list.
Okay Fine: create for testing purposes INTLIST *mylist[2];
I call the same functions:
list_add(&list[0], 1);
list_add(&list[0], 2);
This prints out 2 1 ... Great,
But when I do this:
list_add(&list[1], 3);
list_add(&list[1], 4);
I get a Segmentation fault..

C Linked list only contains first element...not sure what happens to rest

I posted a question a few days ago about a linked list in C. I thought everything was ok then the prof emails us saying that instead of this signature:
int insert_intlist( INTLIST* lst, int n); /* Inserts an int (n) into an intlist from the beginning*/
He accidentally meant:
int insert_intlist( INTLIST** lst, int n); /* Inserts an int (n) into an intlist from the beginning*/
I thought to myself cool now that I have a pointer to a pointer I can move the pointer outside of main and when I return to main I will still have my complete linked list.
He starts off with giving us this:
INTLIST* init_intlist( int n )
{
INTLIST *lst; //pointer to store node
lst = (INTLIST *)malloc(sizeof(INTLIST)); //create enough memory for the node
lst->datum = n; //set the value
lst->next = NULL; //set the pointer
return lst; //return the new list
}
Which is simply to initialize the list like so in main:
if (lst==NULL)
lst = init_intlist(i);
else
insert_intlist(lst, i);
lst is of type INTLIST* so its defined as INTLIST* lst. So I read in some numbers from a text file like 1 3 4 9.
It is supposed to create a linked list from this...so the first number would go to init_intlist(1); And that was defined above. Then it grabs the next number 3 in this case and calls insert_intlist(lst, 3). Well here is my insert_intlist and all I want to do is insert at the beginning of the list:
int insert_intlist(INTLIST** lst, int n )
{
INTLIST* lstTemp; //pointer to store temporary node to be added to linked list
lstTemp = (INTLIST *)malloc(sizeof(INTLIST)); //create enough memory for the node
lstTemp->datum = n; //assign the value
//check if there is anything in the list,
//there should be, but just in case
if(*lst == NULL)
{
*lst=lstTemp;
lstTemp->next=NULL;
}
else
{
lstTemp->next = *lst; //attach new node to the front
*lst = lstTemp; //incoming new node becomes the head of the list
}
return 0;
}
So if the list contained 1 initially this function would simply create a new node and then make this temp node->next point to the head of the list (which I thought was lst) and then reassign the head of the list to this new temp node.
It all looks like it is running right but when I try to print my list to the screen it only prints the number 1.
Anyone have any clues as to what I am doing wrong?
You're being passed a pointer to a pointer. You want to alter the pointer that is being pointed to, not the pointer to a pointer itself. Does that make sense?
if(lst == NULL)
Here you're checking to see if you were passed a NULL pointer. Good practice for error checking, but not for what you were doing right there. If lst is NULL, then you don't even have a pointer to a pointer, can't do anything, and should return a nonzero error code without doing anything else.
Once you're sure your pointer is not NULL, then you look at the pointer it's pointing to (*lst). The pointed-to pointer is the pointer to the first list item. If that pointer is NULL, then you change it to the new item's pointer. Basically, where you use lst you should be using *lst or (*lst). (REMEMBER: the * operator runs after the -> operator! So to get a field in the object pointed to by the pointer that is pointed to by lst [pant, pant], you use (*lst)->whatever.)
P.S. This kind of pointer work is critical to learn to be a good programmer, especially with C.
P.P.S. The other thing you got wrong is that instead of
insert_intlist(lst, i);
you're supposed to call it like
insert_intlist(&lst, i);
... and, for brownie points, check the return code for errors.
The first problem that pops into my mind is that in insert_intlist() you're doing lst = lstTemp;. This should be *lst = lstTemp;. This way you assign to the list pointer that you were supplied rather than to the list pointer pointer (which does not update anything outside of the function).
Since you are using a pointer to another pointer in the insert function, you can change at which memory location the latter actually points to. I changed the insert code a bit and it works fine:
int insert_intlist(INTLIST** lst, int n )
{
INTLIST* lstTemp; //pointer to store temporary node to be added to linked list
lstTemp = (INTLIST *)malloc(sizeof(INTLIST)); //create enough memory for the node
lstTemp->datum = n; //assign the value
//check if there is anything in the list,
//there should be, but just in case
if(*lst == NULL)
{
lstTemp->next=NULL;
*lst = lstTemp;
}
else
{
lstTemp->next = *lst; //attach new node to the front
*lst = lstTemp; //incoming new node becomes the head of the list
}
return 0;
}
EDIT: I see you edited your question. In that case, maybe you are calling the insert function in the wrong way in main(). Try this
int main()
{
INTLIST *head;
head = init_intlist(42);
insert_intlist(&head, 41);
display(head);
return 0;
}
Check to see if lstTemp->next == NULL after lstTemp->next=lst;

Resources