Stop condition for printing linked structs? - c

I have the following struct:
struct cell {
int nmbr;
struct cell *p;
};
I have created a chain of linked structs from this type. Every struct is connected to its predecessor through *p. If I decide to print all nmbrs with a recursive algorithm as shown below, how do I define the stop condition?
void write(struct cell* l) {
/* The following if statement doesn't solve my problem,
but hopefully you see what I'm trying to do */
if (&l != 0x000000) {
printf("%d \t", l->nmbr);
write(l->p);
}
}

You want
if (l != 0)
or
if (l != NULL)
Of course, you also need to make sure that the tail of your linked list has p assigned NULL as well; otherwise it will be uninitialized and probably something not NULL but invalid anyway.

You should check against 'l' not being NULL rather than '&l' assuming that the linked list in NULL terminated.

Assuming the linked list is not cyclic, you just stop when you reach a null pointer.
Change
if (&l != 0x000000)
to
if (l != NULL)

As others have said, you should check l (el), not &l, against 0 or NULL. So, your function should look something like:
void write(CELL* l) {
if (l != 0x000000) {
printf("%d \t", l->nmbr);
write(l->p);
}
}
That said, it is easy to accomplish the same thing using a while, thus avoiding the overhead of recursion:
list_pos = list_head;
while (list_pos != NULL) {
printf("%d \t", list_pos->nmbr);
list_pos = list_pos -> p;
}

the print number up here, for the current node.
if(l->p!=NULL)
write(l->p);

Related

Increment integer pointer in a loop in C

My original code to display nodes in a queue in order from lowest to greatest:
void display (queue *q) {
node *ptr = q->front;
int i = 0;
int size = q->size;
while (i <= size) {
while (ptr->id != i) {
ptr = ptr->next;
}
if (i == 0) {
printf("%d ", ptr->id);
printf("%d\n", ptr->running);
}
else {
printf("%d ", ptr->id);
}
i++;
ptr = q->front;
}
}
Had kept producing Segmentation Fault (Core Dumped) errors. I have malloc the two variables being compared and this error has been fixed.
void display (queue *q) {
node *ptr = malloc(10);
ptr = q->front;
int *i = NULL;
i = malloc(sizeof(int));
*i = 0;
int size = q->size;
while(*i <= size){
while (ptr->id != *i) {
ptr = ptr->next;
}
if (*i == 0) {
printf("%d %d\n", ptr->id, ptr->running);
}
else {
printf("%d %d %d %d\n", ptr->id, ptr->running, ptr->ready, ptr->blocked);
}
i = i + 1 * (sizeof(char));
ptr = q->front;
}
}
However now this doesn't produce the output that I want. I want to increment the i pointer so that it can be the same as my original code.
This has been immensely frustrating, any help would be greatly appreciated!
If I read your first code listing correctly, there's at least one important thing here you need to think about. You seem to have a linked list here, and you're iterating over that list using ptr = ptr->next. This means you need to know when to stop. One common way of setting up a linked list is that the last item in the list has a next value of NULL. Then process the nodes one at a time, and once you are done with one node, you you check whether the next value is NULL: if it isn't you can move on that that next node, if it is NULL you stop.
Here you're not doing checks like this, so you need another way to ensure that you know when to stop. What you seem to be doing is taking the value of q->size and using that to inform you how many items there are in the linked list. So the first thing to think about is how confident you are that that value is correct. For example, if the code building the list puts only two items into the list, bet sets size to three, you'll end up falling off the end of the list, and a segmentation fault is not unlikely.
But there's something even more important than that. Even if you're getting the correct number of items in the list from q->size, you're comparing your loop variable i to size like this:
int i = 0;
while (i <= size) {
⋮
}
This is going to loop with i having the values [ 0, 1, … size ], which is actually size + 1 times. If you want to loop exactly size times, you want a test like i < size rather than i <= size. This is a common mistake, often called an off-by-one error.
Unfortunately, your second listing complicates things, rather than making them better. Go back to your first one and see if you can fix the things I've mentioned here.

C: Help understanding pointers

I have been building this hash table function that is given an array it gets a word from and a pointer to an array that it fills with my linked list struct. It compiles correctly but I get a segmentation fault at - *hashTable[hashVal] = *newNode; .
void hashTableCreate(char *array, list *hashTable[]) {
while(arrayPos < getArrayLength(array)) {
char *temp = getWord(array);
int hashVal = hashingFunc(temp);
if((*hashTable[hashVal]).word == temp ) {
(*hashTable[hashVal]).count = (*hashTable[hashVal]).count+1;
}
else {
list *newNode = malloc(sizeof(list));
strcpy(newNode->word,temp);
newNode->count = 1;
*hashTable[hashVal] = *newNode;
}
}
}
It's called like this:
void timeStructures(char *newArray) {
list *hashTable[3000];
hashTableCreate(newArray, hashTable);
}
I know this is to do with the way I am using my pointers but I can't figure it out. Any help would be appreciated.
There are several problems on this code:
You seem you are using extra asterisks when assigning newNode to hashTable.
You can use ++ operator to increment count, and use -> to access a member of a pointer to struct.
getWord is unknown to me, but seems impossible it can return a pointer to a string contained inside an element of hashTable, which means the next if will always be false (newNode's word is a copied string, not a pointer to array's word), as it compares memory addresses instead of strings.
You never free temp neither hashTable elements, which seems another misconception of what you are doing here.
As noted by #kaylum , you should initialize hashTable so you avoid comparing against unassigned array elements, neither modifying count on them.
hashTable has to be passed as a pointer to hashTableCreate, or this will work on a copied version of it:
Try this:
void hashTableCreate(char *array, list *(*hashTable[])) {
while(arrayPos < getArrayLength(array)) {
char *temp = getWord(array);
int hashVal = hashingFunc(temp);
if((*hashTable)[hashVal] != NULL &&
strcmp((*hashTable)[hashVal]->word, temp) == 0 ) {
(*hashTable)[hashVal]->count++;
}
else {
list *newNode = malloc(sizeof(list));
strcpy(newNode->word,temp);
newNode->count = 1;
(*hashTable)[hashVal] = newNode;
}
free(temp);
}
}
void timeStructures(char *newArray) {
list *hashTable[3000];
int i;
// Initialize hashTable pointers to NULL
for(i = 0; i < 3000; i++) {
hashTable[i] = NULL;
}
hashTableCreate(newArray, &hashTable);
// Free hashTable elements, malloc'ed at hashTableCreate
for(i = 0; i < 3000; i++) {
if(hashTable[i] != NULL) {
free(hashTable[i]);
}
}
}
Note 1: you never check if hashVal is higher than 3000, which might be fine if you know what you are doing, aka, you know hashingFunc will never return 3000 or higher. If you do not, you'll need to care about growing hashTable appropriately.
Note 2: executing getArrayLength on each loop might be a performance problem for large lists. You probably prefer to create a variable to contain its value and execute it only once.
Note 3: It might be some easier if you declare hashTable as a plain list of elements, not a list of pointers to elements, like list hashTable[3000];, but I don't know exactly what you're looking for. Also, it will always waste 3000 list elements of memory, so might be a performance problem.

linked list insertion sort in c

the program should do the insertion ascending sort for the nodes,first it should check the names and if the names are equal it should sort the ids,i do not know what is the issue that does not sort properly.
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
typedef struct nd{
int id;
char name[20];
float gpa;
struct nd *next;
}node;
typedef node *list;
//---------------------------------------------------------------
int insertlist(list *head,char *buffer)
{
list p,q,n;
int m,num,k,sc;
p=(list)malloc(sizeof(node));
num=sscanf(buffer,"%d %s %f",&(p->id),(p->name),(&p->gpa));
if(num!=3)
{
printf("info not complete\n");
free(p);
return 1;
}
else
{
if(!*head)
{
*head=p;
p->next = NULL;
}
//******** sorting tthe names and ids for equal names
else if(sc=strcmp((*head)->name,p->name)> 0 || ((sc == 0) && ((*head)->id > p->id)))
{//head is modified
p->next=*head;
*head=p;
}
else{
n=*head;
q=n->next;
while(q && ((sc=strcmp(q->name,p->name)<0) || ((sc == 0) && (q->id < p->id))))
{
n=q;
q=q->next;
}
n->next=p;
p->next=q;
}
}
return 0;
}
//------------------------------------------------------
int main()
{
int id,r;
list head,p;
FILE *fp;
char c,buffer[100],filename[10];
if ((fp=fopen("student.txt","r"))==NULL)
{
printf("error opening %s",filename);
exit(1);
}
else
{
head=NULL;
while(fgets(buffer,100,fp)!=NULL)
{
buffer[strlen(buffer)-1]=='\0';
r=insertlist(&head,buffer);
}
fclose(fp);
}
for(p=head;p!=NULL;p=p->next)
printf("%d %s %f\n\n",p->id,p->name,p->gpa);
}
An example of the contents of student.txt:
121513 ala 45.00
121510 wang 21.00
145852 frank 26.00
151515 ala 25.00
Your sorting issue is one of operator precedence
< and > have a higher precedence than =, meaning it will be evaluated first, then an assignment will take place.
So your string compares in these two places:
else if(sc=strcmp((*head)->name,p->name)> 0 || ((sc == 0) && ((*head)->id > p->id)))
...
while(q && ((sc=strcmp(q->name,p->name)<0) || ((sc == 0) && (q->id < p->id))))
are wrong. sc is getting the value of strcmp((*head)->name,p->name)> 0 and strcmp(q->name,p->name)<0 respectively (note this is going to always be 1 or 0, never -1)
If you simply adjust your code as such:
else if((sc=strcmp((*head)->name,p->name))> 0 || ((sc == 0) && ((*head)->id > p->id)))
...
while(q && (((sc=strcmp(q->name,p->name))<0) || ((sc == 0) && (q->id < p->id))))
You'll see it working. Moral of the story: don't try to be stingy with your parens or brackets, it doesn't cost you anything to put more in, it makes the code clearer, and it saves you debugging headaches like this one.
head is a pointer so in order to change it in a function you need to pass a pointer to it. A pointer to the pointer.
Declare like this:
int insertlist(list **head,char *buffer)
Call it like this:
r=insertlist(&(&head),buffer);
And then in the function change everywhere you reference it to de-reference the pointer.
First, fix this:
buffer[strlen(buffer)-1]=='\0';
That's an equality comparison; not an assignment. I believe you're attempting to throw out the newline at the end of the buffer. If that is the case you may want to make sure it has a newline to throw out to begin with (the last line of the input file, for example may not end in one. Regardless, this is still broken, and needs to be fixed.
Next, your sort loop has issues. I'm including one below that is hopefully easier to read, and therefore understand, with logic flaws removed (and quite a bit of other extracurricular activity as well):
int insertlist(list *head, char *buffer)
{
list p=NULL, q=NULL;
int sc=0;
/* allocate new node */
p = calloc(1, sizeof(*p));
if(3 != sscanf(buffer,"%d %s %f",&(p->id),(p->name),(&p->gpa)))
{
printf("info not complete\n");
free(p);
return 1;
}
/* initially wire p->next to our list head. then, walk list,
advancing p->next. break on first "less" condition */
p->next = *head;
while (p->next)
{
/* broken out here for clarity; break on first "less" */
sc = strcmp(p->name, p->next->name);
if (sc < 0 || (sc == 0 && p->id < p->next->id))
break;
q = p->next;
p->next = q->next;
}
/* non-null means we wire q->next to p */
if (q)
q->next = p;
/* else p is the new head; what head was prior is already in p->next */
else
*head = p;
return 0;
}
Tested with the following input file:
0001 Brook 3.50
0002 James 3.51
0003 Katie 3.52
0004 James 3.87
0005 Brook 2.70
Results:
1 Brook 3.500000
5 Brook 2.700000
2 James 3.510000
4 James 3.870000
3 Katie 3.520000
I strongly suggest you single-step through the code in a debugger when trying to fix these problems and when you want to see how code works.
Finally, not to add insult to injury, you never free your list. I.e it leaks memory on program exit, which is second only to leaking memory during execution in levels of "bad". Walk that list and release that memory. It is a good habit to get into.
EDIT OP Request for freeing the linked list:
For now, at the end of main() before the return statement will suffice. At some time you should consider writing a function to do this for you:
while (head)
{
list p=head;
head=head->next;
free(p);
}

Segfault with a quicksort implementation in C

I am debugging my code for a quick sort algorithm in C. It compiles but fails with a "Segmentation fault" when running it.
Can anybody help me to debug it and give me the working version of my code? I know there are existing and working ones on the internet. But what I really want is to find the bug of my own code.
void myQuickSort(int list[],int head, int tail)
{
int m = head;
int n = tail;
int key = list[m];
++head;
while(head < tail)
{
while(list[head] < key)
{
++head;
}
while(list[tail] >= key)
{
--tail;
}
//swamp two elements, to divide the array to two groups
int temp = list[head];
list[head] = list[tail];
list[tail] = temp;
}
//get the pivot element in dividing position
int temp = list[m];
list[m] = list[head];
list[head] = temp;
myQuickSort(list, m, head-1);
myQuickSort(list, head+1, n);
}
Your function will never exit.
It will keep calling itself until the call stack is full and cause a stack overflow exception.
The compiler should generate a warning for this:
warning C4717: 'myQuickSort' : recursive on all control paths, function will cause runtime stack overflow
You need an exit condition, something along the lines of:
void myQuickSort(int list[],int head, int tail)
{
//exit condition, or else the function will always call itself
if ( head >= tail )
return;
/**
...
*/
myQuickSort(list, m, head-1);
myQuickSort(list, head+1, n);
}
Also, make sure you call the function like:
int num[5] = {1,4,2,3,5};
myQuickSort(num,0,4);
the final parameter must be 1 less than the length of the array since C++ arrays are 0-based.
You also need one extra check in your while loops:
while( head < tail && list[head] < key ) // see if head reached the end
{
++head;
}
while( head < tail && list[tail] >= key )
{
--tail;
}
or else you might pass the end of the array.
Just looking at it quickly, I see a number of places where this could segfault. For example, here:
while(list[head] < key)
{
++head;
}
Imagine a list where key was, by chance, the largest element in the list. This loop will then run until head is past the end of the array, at which point a segfault can occur at any time. Likewise, the following loop can cause tail to move off the beginning of the array.
In addition to the guaranteed stack overflow diagnosed by Luchian, you need to check that you don't run off the array in the inner loop:
while(head <= tail && list[head] < key)
while(head <= tail && list[tail] >= key)

Understanding double pointer in doubly linked list in C

I have an exam tomorrow and I was trying to understand this doubly linked list example that the instructor placed on the class website but I'm having a hard time understanding a bit of it...
Here's the code:
#include <stdio.h>
#include <stdlib.h>
typedef struct dl {
int key;
float value;
struct dl *next;
struct dl *prev;
} DL;
DL *insert(int c, float f, DL *l) {
DL *new = (DL*) malloc(sizeof(DL));
if (new == NULL) exit(-1);
new->key=c; new->value=f;
if (l==NULL) {
new->next=NULL; new->prev=NULL;
}
else if (l->key < c) {
while((l->next != NULL) && (l->next->key < c)) { l=l->next; }
new->next=l->next; l->next=new; new->prev=l;
if (new->next != NULL) {
new->next->prev=new;
}
}
else {
while((l->prev != NULL) && (l->prev->key > c)) { l=l->prev; }
new->prev=l->prev; l->prev=new; new->next=l;
if(new->prev != NULL) {
new->prev->next=new;
}
}
return new;
}
int search(int c, float *f, DL **lptr) {
if (*lptr == NULL) return 0;
if (c < (*lptr)->key) {
while(((*lptr)->prev!=NULL)&&((*lptr)->prev->key >= c)) {
(*lptr)=(*lptr)->prev;
}
}
else if (c > (*lptr)->key) {
while(((*lptr)->next!=NULL)&&((*lptr)->next->key <= c)) {
(*lptr)=(*lptr)->next;
}
}
if ((*lptr)->key == c) {
*f = (*lptr)->value;
return 1;
}
return 0;
}
void printList(DL *l) {
if (l == NULL) return;
while (l->prev != NULL) { l=l->prev; };
while(l != NULL) {
printf("%d,%f\n",l->key,l->value);
l=l->next;
}
}
int main(void) {
DL *list=NULL;
float f;
list=insert(3,5.6,list); list=insert(4,5.3,list);
list=insert(7,3.6,list); list=insert(1,7.7,list);
list=insert(9,2.3,list); list=insert(0,9.0,list);
printList(list);
if (search(3,&f,&list)) {
printf("Found %f.\n",f);
}
else {
printf("Not found.\n");
}
printList(list);
return 0;
}
An here's the output:
0,9.000000
1,7.700000
3,5.600000
4,5.300000
7,3.600000
9,2.300000
Found 5.600000.
0,9.000000
1,7.700000
3,5.600000
4,5.300000
7,3.600000
9,2.300000
What I don't get is the "search" function. The list being passed is a pointer to a pointer of DL, right? And we are looking for a number, for that we keep doing (*lptr) = (*lptr)->next (or prev) to iterate through the whole list. What I don't get is why the second call to printList() prints the whole list... After the search() call has been made, shouldn't the "list" only have the elements after the one we looked for? The pointer was changed, how come when we return from search() the pointer is restored to the first element and the whole list is printed?
This is what I don't get cause if I change the search() function and add (*lptr) = NULL in the first line, the second call to printList() will not print anything, cause the pointer was changed, it is NULL now, there's nothing to print. Why doesn't (*lptr) = (*lptr)->next has a similar effect? The pointer is also being changed to the next one, shouldn't the second printList() call only print the remaining elements in the list?
EDIT:
Every answer seems to be the correct one and I'm going to sort it by "oldest" and accept the "fastest" one, don't be mad, I need to have some criteria. I could go on and see which answered provided better insight on the issue but it's irrelevant because I already know everything that was said. I was just stupid enough to not even look to the printList() function and assumed it was ok, I also assumed that the error was somewhere on the search() function. But I knew I was right, I knew the pointer was being change and the list couldn't print everything, but I understand why now...
printList rewinds the list before printing it.
while (l->prev != NULL) { l=l->prev; };
If it didn't have the above line, it would just print the things after the found element.
This line return pointer back:
while (l->prev != NULL) { l=l->prev; };
And those do the printing:
while(l != NULL) {
printf("%d,%f\n",l->key,l->value);
l=l->next;
}
And there is much better approach of doing this, just by adding the additional field or even two which will always point at the beginning and end of the list.
As far as I can read (and like rmeador commented, it is pretty awful code), the search call does modify the list pointer to point to the found element.
The trick is the printList function.
The first thing it does (other than checking for NULL) is this:
while (l->prev != NULL) { l=l->prev; };
So it basically follows the prev pointer back to the start of the list, so the actual printing starts from the start of the list even if it is passed a pointer to the middle or end of it.
In the printList() function you are going back from the found element using l = l->prev. Then you are printing all the contents.
What I don't get is why the second
call to printList() prints the whole
list... After the search() call has
been made, shouldn't the "list" only
have the elements after the one we
looked for? The pointer was changed,
how come when we return from search()
the pointer is restored to the first
element and the whole list is printed?
What you have is not really a pointer to the list, but a pointer to an element in the list. The first thing that the printList function does it to loop back through the prev references to find the first element of the list.
He backtrack the pointer inside the print:
while (l->prev != NULL) { l=l->prev; };
Remember that the list is doubly linked. Search doesn't change the list, just what part of it "list" is presently pointing to.

Resources