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 1开发者_运维技巧0 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..
精彩评论