Thread: problem with pointers in linked list

  1. #31
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by django View Post
    struct order *order = malloc(5 * sizeof(struct order));
    struct bookOrder *bookOrd = malloc(5 * sizeof(struct bookOrder));

    //fill records
    order[0].price = 1;
    order[0].size = 50;
    bookOrd[0].order = &order[0];
    bookOrd[0].next = NULL;

    order[1].price = 10;
    order[1].size = 100;
    bookOrd[1].order = &order[1];
    bookOrd[1].next = NULL;
    bookOrd[0].next = &bookOrd[1];

    order[2].price = 20;
    order[2].size = 200;
    bookOrd[2].order = &order[2];
    bookOrd[2].next = NULL;
    bookOrd[1].next = &bookOrd[2];
    ...[/code]
    No, not like that. Whilst you can optimise memory usage as part of a custom allocator, don't try and do that as part of the linked list itself.
    You need to allocate memory for every single one i n d i v i d u a l l y, otherwise you can't delete items from the list properly. Yes that would mean calling malloc ten times, but no it shouldn't mean writing malloc ten times in your program, because you would put the code that alocated builds and links the node into a separate function, and so there would only be two mallocs in there, but it would be called five times. Forget about array indexing, there shouldn't be a single [] pair in the entire function involved in building the list. Rather, learn to use the -> operator.

    Remove that allocation inside main as well, all that does is create a leak.
    Last edited by iMalc; 07-28-2011 at 12:45 AM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  2. #32
    Registered User
    Join Date
    Jul 2011
    Posts
    99
    Quote Originally Posted by AndrewHunter View Post
    I would tend to agree with Tater's comments on your stuct design. This is why starting 2 threads on the same topic isn't ideal. Did you happen to look at your other thread? I think many of these issues with program design that have been brought up have been addressed there.
    Yes, this is the first time and the last time that I started two all too similar thread, very annoying indeed. I read your extensive reply, thanks, very helpful.

  3. #33
    Registered User
    Join Date
    Jul 2011
    Posts
    99
    Ok, starting to get it. I will post my solution in the hope that other may benefit from this thread and see where it leads. I find the biggest problem with this matter to actually understand in plain english, what is really happening. Since I try not to program by trial and error, but from a good understanding, I will post my train of thought here and hope that you will correct me in case of misunderstanding.

    From the ground up:
    1. What do I want?
    I want to implement a linked list with a separate function that can be called from main() to add extra nodes to the list. This means that I need to be able to fill a linkedList that is created in main() from a separate function.
    2. The theory
    (a)Variables to functions in C are passed by value. As soon as a function terminates, the locally declared variables are freed and nothing remains. So, if I want to populate a list from main(), you'll have to make sure this does not happen.
    (b)Pointers are no exception to this. When you pass a pointer to a function you pass the value of the pointer - the adress it points to - to the function. As with other variables, you can manipulate this inside the function but when it comes back the pointer will be unchanged, i.e. the pointer will still contain the same adress (and therefore point to the same adress).
    (c) You can however use the adress information of the pointer to manipulate what's at that adress. You can manipulate the content of the pointed-to memory. Again, the pointer remains the same, but what it refers to can be directly manipulated from a function and will remain changed after exiting the function.
    (d) Per consequence, if I pass a pointer to a pointer, I can manipulate two things. First I can manipulate the content of memory that the first pointer refers to, i.e. the value of the second pointer, i.e. the adress that that pointer refers to and per consequence I can manipulate the contect of the adress that the second pointer points to as described in (c).

    3.Ok, how to do it?
    Solution 1 - pointer to pointer, all memeory dynamically allocated in the function
    I pass a pointer to a pointer to the list (list is book). In the function I allocate for both structures (the actual order and the wrapper bookOrder) and I add these to the tail of book. This works, see below.
    Code:
    //Solution 1 - pointer to pointer, all memeory dynamically allocated in the function
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct order {
    	char dir;
    	float price;
    	int size;
    	char instr[5];
    };
    
    struct bookOrder {
    	struct bookOrder *next;
    	struct order *order;
    };
    
    void printBook(struct bookOrder *book) {
    	int i = 0;
    	printf("------------------------------------\n");
    	while ((book != NULL) && (i < 11)){
    		printf("Printbook price:%f size:%i \n",(book->order)->price, (book->order)->size);
    		book = book->next;
    		++i;
    		if (i == 10) {
    			printf("Printing function may be artifically terminated");
    		}
    	}
    	if (i == 0) {
    		printf("Book is empty\n");
    	}
    	printf("------------------------------------\n");
    }
    
    void addOrder(struct bookOrder **book, struct order ord) {
    	
    	struct bookOrder *walker = *book;
    	struct bookOrder *newBook = malloc(sizeof(**book));
    	struct order *newOrder = malloc(sizeof(ord));
    	
    	newOrder->price = ord.price;
    	newOrder->size = ord.size;
    	
    	newBook->order = newOrder;
    	newBook->next = NULL;
    	
    	if (*book == NULL) {
    		*book = newBook;
    	}
    	else {
    		while (walker->next != NULL)
    			walker = walker->next;
    		walker->next = newBook;
    	}
    }
    
    
    int main(void) {
    
    	//print empty book
    	struct bookOrder *book = NULL;
    	printBook(book);
    
    	//insert first order and print
    	struct order inpOrder;
    	inpOrder.price = 37;
    	inpOrder.size = 600;
    	addOrder(&book, inpOrder);
    	printBook(book);
    
    	//insert second order and print
    	inpOrder.price = 67;
    	inpOrder.size = 340;
    	addOrder(&book, inpOrder);
    	printBook(book);
    
    	return 0;
    }
    Solution 2 - single pointer, all memory dynamically allocated in the function
    When I think about this, I sense that I do not fully graps it. Listen to this.
    The pointer in main() refers to 1 bookRecord. When adding nodes to this list, I do not mean to change this pointer at all. What I want to do is to take this pointer, travel to the end of the list by following the bookOrder->next field untill it refers to NULL and then attach the new record to it. If I make sure that I allocate memory, this seems possible which would imply that I do not have to pass a pointer to a pointer to a list, but only a pointer to the list. I have done that below, but this does not work, where is my reasoning flawed? Again, a bit lengthy and it may seem like I am losing myself in it, but as I said, I just need to really understand how this works.
    Code:
    //Solution 2 - single pointer, all memory dynamically allocated in the function
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct order {
    	char dir;
    	float price;
    	int size;
    	char instr[5];
    };
    
    struct bookOrder {
    	struct bookOrder *next;
    	struct order *order;
    };
    
    void printBook(struct bookOrder *book) {
    	int i = 0;
    	printf("------------------------------------\n");
    	while ((book != NULL) && (i < 11)){
    		printf("Printbook price:%f size:%i \n",(book->order)->price, (book->order)->size);
    		book = book->next;
    		++i;
    		if (i == 10) {
    			printf("Printing function may be artifically terminated");
    		}
    	}
    	if (i == 0) {
    		printf("Book is empty\n");
    	}
    	printf("------------------------------------\n");
    }
    
    void addOrder(struct bookOrder *book, struct order ord) {
    	
    	struct bookOrder *walker = book;
    	struct bookOrder *newBook = malloc(sizeof(book));
    	struct order *newOrder = malloc(sizeof(ord));
    	
    	newOrder->price = ord.price;
    	newOrder->size = ord.size;
    	
    	newBook->order = newOrder;
    	newBook->next = NULL;
    	
    	if (book == NULL) {
    		book->order = newBook->order;
    		book->next = NULL;
    	}
    	else {
    		while (walker->next != NULL)
    			walker = walker->next;
    		walker->next = newBook;
    	}
    }
    
    
    int main(void) {
    
    	//print empty book
    	struct bookOrder *book = NULL;
    	printBook(book);
    
    	//insert first order and print
    	struct order inpOrder;
    	inpOrder.price = 37;
    	inpOrder.size = 600;
    	addOrder(book, inpOrder);
    	printBook(book);
    
    	//insert second order and print
    	inpOrder.price = 67;
    	inpOrder.size = 340;
    	addOrder(book, inpOrder);
    	printBook(book);
    
    	return 0;
    }
    Thanks for your patience.

  4. #34
    Registered User
    Join Date
    May 2011
    Location
    Around 8.3 light-minutes from the Sun
    Posts
    1,949
    The confusion you are having comes down to really wrapping your head around 'pass by value'. Anything you pass to a function, including a pointer, is pass by value. Thus any changes made in that function are gone once the function returns. This includes making assignments to pointers, which is what you are doing in your second example.

    A simplier example of the same concept is :

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    void foo(int* ptr){
    
    	ptr = malloc(sizeof(int));
    	*ptr = 5;
    }
    int main(void){
    
    	int* mainptr = NULL;
    	
    	if(mainptr==NULL)
    		printf("mainptr is NULL\n");
    	
    	foo(mainptr);
    	
    	if(mainptr==NULL)
    		printf("mainptr is NULL\n");
    
    	getchar();
    	return(0);
    }
    For a similar explanation, you can read c-faq Question 4.8
    Last edited by AndrewHunter; 07-28-2011 at 12:19 PM.
    Quote Originally Posted by anduril462 View Post
    Now, please, for the love of all things good and holy, think about what you're doing! Don't just run around willy-nilly, coding like a drunk two-year-old....
    Quote Originally Posted by quzah View Post
    ..... Just don't be surprised when I say you aren't using standard C anymore, and as such,are off in your own little universe that I will completely disregard.
    Warning: Some or all of my posted code may be non-standard and as such should not be used and in no case looked at.

  5. #35
    Registered User
    Join Date
    Jul 2011
    Posts
    99
    Got it. What I tried to do was to change the value of the pointer in the function, which you cannot do in a pass-by-value context. The same is happening in your example. However, when you have the pointer in main() refer to an empty wrapper (bookOrder is a wrapper around order), then it works and you do not need any pointers to pointers. Just tinkering a bit in order to understand it. Not necessarily the cleanest solution, but it works. Once again, thanks for the support.
    Code:
    //Solution 2 - single pointer, all memory dynamically allocated in the function
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct order {
    	char dir;
    	float price;
    	int size;
    	char instr[5];
    };
    
    struct bookOrder {
    	struct bookOrder *next;
    	struct order *order;
    };
    
    void printBook(struct bookOrder *book) {
    	int i = 0;
    	printf("------------------------------------\n");
    	if (book->order == NULL)
    		printf("Book is empty\n");
    	else {		
    		while ((book != NULL) && (i < 11)){
    			printf("Printbook price:%f size:%i \n",(book->order)->price, (book->order)->size);
    			book = book->next;
    			++i;
    			if (i == 10) 
    				printf("Printing function may be artifically terminated");
    		}
    	}
    	printf("------------------------------------\n");
    }
    
    void addOrder(struct bookOrder *book, struct order ord) {
    	
    	struct bookOrder *walker = book;
    	struct bookOrder *newBook = malloc(sizeof(book));
    	struct order *newOrder = malloc(sizeof(ord));
    	
    	newOrder->price = ord.price;
    	newOrder->size = ord.size;
    	
    	newBook->order = newOrder;
    	newBook->next = NULL;
    	
    	if (book->order == NULL) {
    		book->order = newOrder;
    	}
    	else {
    		while (walker->next != NULL)
    			walker = walker->next;
    		walker->next = newBook;
    	}
    }
    
    
    int main(void) {
    
    	//create book with empty wrapper
    	struct bookOrder *book = NULL;
    	struct bookOrder inpBook;
    	inpBook.order = NULL;
    	inpBook.next = NULL;
    	book = &inpBook;
    	printBook(book);
    
    	//insert first order and print
    	struct order inpOrder;
    	inpOrder.price = 13;
    	inpOrder.size = 129;
    	addOrder(book, inpOrder);
    	printBook(book);
    	
    	//insert second order and print
    	inpOrder.price = 37;
    	inpOrder.size = 600;
    	addOrder(book, inpOrder);
    	printBook(book);
    
    	//insert third order and print
    	inpOrder.price = 67;
    	inpOrder.size = 340;
    	addOrder(book, inpOrder);
    	printBook(book);
    
    	return 0;
    }

  6. #36
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Well I'm very impressed, you've not only got the theory nailed down now, but you've followed the advice and rapidly gotten from where you were to a fairly good solution.
    You certainly appear far more capable than most who come here for help. Stick around, we need more people like you here. There's always surprisingly more to learn and plenty of others who even you have the knowledge to assist now.

    One more thing that should be commonly done in a real program is to check the return value of malloc to make sure that it isn't NULL before using the memory you asked for. In practice this program shouldn't fail to allocate memory, but in a real program it is a real possibility. How you choose to handle getting NULL from malloc is up to you though. You could do anything from terminating the program instantly to gracefully handling it, letting the user know that the operation failed.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  7. #37
    Registered User
    Join Date
    Jul 2011
    Posts
    99
    Thanks, really stimulating to read this. I am new to C, I love the compactness of it but it is a bit of an effort to get started when you really want to understand what's going on. I am aware of the total lack of error checking and handling, but this is next on my learning list.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Help a Student in C (Linked List, Pointers etc)
    By mocha1218 in forum C Programming
    Replies: 14
    Last Post: 07-02-2010, 01:38 PM
  2. Linked List with Two Pointers
    By aspekt9 in forum C Programming
    Replies: 5
    Last Post: 12-07-2009, 04:23 PM
  3. Replies: 7
    Last Post: 10-09-2009, 06:27 AM
  4. linked list and pointers : ??????
    By gemini_shooter in forum C Programming
    Replies: 7
    Last Post: 05-04-2005, 05:02 AM
  5. Replies: 6
    Last Post: 03-02-2005, 02:45 AM