So I'm pretty new to C, but not to programming. I am trying to learn C and so I decided to try implementing a simple linked list.
Here is the code:
#include <stdio.h>
typedef struct node node;
struct node {
char *word;
node *next;
};
// Returns a node.
node node_new(char *word) {
node n;
n.word = word;
n.next = NULL;
return n;
}
// Traverses the linked list, spitting out the
// words onto the console.
void traverse(node *head) {
node *cur = head;
while (cur != NULL) {
printf("I have %s.\n", cur->word);
cur = cur->next;
}
printf("Done.\n");
return;
}
// In here I get circular references whenever I pass a second argument.
void dynamic(int argc, char **argv) {
printf("DYNAMIC:\n");
node n = node_new("ROOT");
node *cur = &n;
int i;
for (i = 0; i < argc; i++) {
node next = node_new(argv[i]);
cur->next = &next;
cur = &next;
}
traverse(&n);
}
void predefined(void) {
printf("PREDEFINED:\n");
node n = node_new("ROOT");
node a = node_new("A");
node b = node_new("B");
node c = node_new("C");
n.next = &a;
a.next = &b;
b.next = &c;
traverse(&n);
}
int main(int argc, char **argv) {
predefined();
dynamic(argc, argv);
return 0;
}
If I just run it without arguments ("./test") the output is:
PREDEFINED:
I have ROOT.
I have A.
I have B.
I have C.
Done.
DYNAMIC:
I have ROOT.
I have ./test.
Done.
but if I put any arguments on, instead of "I have ./test." it gives an infinite loop of whatever the last argument on the command line was ("./test one two three" gives "i have three." over and over ignores the "one" and "two", but the preceding lines are the same).
I think it has to do with bad pointer management in the dynamic function, but I can't figure out why it's setting itself to its own "next" node.
The problem is here:
for (i = 0; i < argc; i++) {
node next = node_new(argv[i]);
cur->next = &next;
cur = &next;
}
By allocating next like this, it remains tied to the stack and doesn't actually change address on each iteration. It should be a new object each time:
for (i = 0; i < argc; i++) {
node *next = malloc (sizeof node);
next->word = argv[i];
next->next = NULL;
cur->next = next;
cur = next;
}
Also, node_new() can't be used because it doesn't allocate any lasting new memory either.
The problem is in your for loop. Every iteration uses the same memory location on the stack to store the next variable. So, effectively, the memory location given by &next is a constant for your entire for loop, and by the time you run traverse, that memory location contains last value of next.
Your for loop is equivalent to this version, which might shed more light:
int i;
node next; // note this line
for (i = 0; i < argc; i++) {
next = node_new(argv[i]);
cur->next = &next;
cur = &next;
}
You'll need to create new nodes on the heap, if you want to be able to pass their addresses around, or store their addresses in other data structures. Read up on malloc and free.
Related
I'm trying to pop the last node from a doubly and circular linked list but I'm getting a headache with pointers. I've got a function that traverses the entire list til the end of it but somehow, the tmp pointer doesn't move or update. I'm traversing the entire list with a for loop instead of a while one as its more comfortable to me (and yes, ive tried with a while loop)
Here is my code:
typedef struct List {
unsigned int size;
Node *p_head;
Node *p_tail;
} List;
typedef struct Node {
void *p_value;
struct Node *p_next;
struct Node *p_previous;
} Node;
Bool RemoveAtEnd(List *list) {
Node *p_node = list->p_head; /* tmp ptr */
for (int i = 0; i < GetSize(list); ++i) {
p_node = (p_node)->p_next;
}
/*
* p_node var should be the last node or tail of the list shouldn't it?
*/
printf("Tail here is %d\n", *(int *) p_node->p_value);
list->p_tail = p_node->p_previous;
list->p_tail->p_next = list->p_head;
list->p_head->p_previous = list->p_tail;
list->size--;
DestroyNode(p_node);
return TRUE;
}
When trying to free the node within the function valgrind says "Invalid read" when trying to traverse the list outside the function because as far i seen, the tail is pointing back to the head instead of the tail
List *p_intList = CreateList();
for (int i = 0; i < 5; ++i) {
int a = i + 1;
InsertAtEnd(p_intList, &a, sizeof(int));
}
RemoveAtEnd(p_intList);
for (int i = 0; i < GetSize(p_intList); ++i) {
printf("%d\n", *(int *) GetValueAt(p_intList, i)); // Invalid read
}
DestroyList(p_intList);```
in pseudocode i would do like this
you know that last-1 -> last -> head
and what you need is the list
to become like last-1 > head
save the last node
last = list.getHead()->prev;
link last-1 with node head
last-1 = list.getHead()->prev->prev;
last-1.next = list.getHead();
list.getHead().prev = last-1
free removed node
free(last);
I am trying to accomplish a function that grows a linked list while also putting them in ascending order at the same time, I have been stuck for a while and gained little progress. I believe my insertLLInOrder is correct it's just the createlinkedList that is messing it up.
Sometimes my output comes out fully and other times it only prints out some of the list.
Anything helps!
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next;
} node;
node *createlinkedList(int num);
node *insertLLInOrder(node * h, node * n);
void display(node * head);
int randomVal(int min, int max);
int
main()
{
int usernum = 0;
node *HEAD = NULL;
printf("How many Nodes do you want? ");
scanf("%d", &usernum);
srand(time(0));
HEAD = createlinkedList(usernum);
display(HEAD);
return 0;
}
node *
createlinkedList(int num)
{
int i;
int n = num;
node *head = NULL;
node *newNode;
node *temp;
for (i = 0; i < n; i++) {
newNode = (node *) malloc(sizeof(node));
newNode->next = NULL;
newNode->data = randomVal(1, 9);
temp = insertLLInOrder(head, newNode);
head = temp;
}
return head;
}
int
randomVal(int min, int max)
{
return min + (rand() % (max - min)) + 1;
}
node *
insertLLInOrder(node * h, node * n)
{
//h is the head pointer, n is the pointer to new node
node *ptr = h;
node *previous = NULL;
while ((ptr != NULL) && (ptr->data < n->data)) {
previous = ptr; // remember previous node
ptr = ptr->next; // check for the next node
}
if (previous == NULL) {
//h is an empty list initially
n->next = NULL;
return n; // return the pointer of the new node
}
else {
//if there are nodes in the linked list
// previous will point to the node that has largest value, but smaller than new node
n->next = previous->next; // insert new node between previous, and previous->next
previous->next = n;
return h; // return old head pointer
}
}
void
display(node * head)
{
node *p = head;
while (p != NULL) {
printf("%d, ", p->data);
p = p->next;
}
}
Obviously in your insertLLInOrder() if the first while loop gives previous == NULL it means that you must insert at list head, which is not what your are doing.
Just change n->next = NULL; to n->next = h; and it should improve behavior.
Taking a step back and perspective
This is a very simple error, but it is made harder to spot because of the way you wrote your code.
The bug in itself is not very interesting, but it can help to get a higher perspective on why it happened and how to avoid such bugs.
And, no, running a debugger is not very helpful for such cases!
Having to run a debugger happens sometimes, but it merely means that you have lost the control of your program. Like having a parachute can be a safety mesure for a pilot, but if he has to use it, it also means that the pilot lost control and his plane is crashing.
Do you know the story of the Three Ninjas Programmers?
The three Ninjas
The chief of ninjas orders three Ninja to show him their training level. There is a Noob, a Beginner and a Senior. He asks them to reach a small cabin, on the other side of a field, take some object inside and come back.
The first Ninja is a noob, he runs and jumps across the field with all his speed but soon enough he walks on a (plaster) mine. He goes back at the start line and confesses his failure, which is obvious because his previously black shirt is now covered by white plaster.
The second Ninja shows some practice. You can tell he failed like the Noob on a previous try and that now he is wary. He is very slow and very careful. He sneaks very slowly across the field watching closely everywhere at each step. He gets quite close to the cabin, and everybody believes he will succeed, but eventually, he is also blown by a mine at the last second. He also goes back disappointed to the starting point, but he somehow believes it will be hard for the third Ninja to do any better.
The third Ninja is a Senior. He walks calmly across the field in a straight line, enter the cabin, and goes back without any visible trouble, still merely walking across the field.
When he gets back to the starting point the other two Ninjas are stunned and ask him eagerly:
- How did you avoid the mines?
- Obviously, I didn't put any mines on my path in the first place; why did you put mines in yours?
Back to the code
So, what could be done differently when writing such a program?
First using random values in code is a bad idea. The consequence is that the code behavior can't be repeated from one run to the next one.
It is also important that the code clearly separate user inputs (data) and code manipulating that data.
In that case, it means that the createLinkedList() function should probably have another signature. Probably something like node *createlinkedList(int num, int data[]) where the data[] array will contains values to sort. It is still possible to fill input data with random values if it is what we want.
That way, we can easily create tests set and unit tests, like in code below:
Home made unit tests suite
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next;
} node;
node *createlinkedList(int num, int * data);
node *insertLLInOrder(node * h, node * n);
/* No need to have a test framework to write unit tests */
/* Check_LL is some helper function comparing a linked list with test data from an array */
int check_LL(node * head, int num, int * data)
{
node *p = head;
int n = 0;
for (; n < num ; n++){
if (!p){return 0;}
if (p->data != data[n]){return 0;}
p = p->next;
}
return p == NULL;
}
void test_single_node()
{
printf("Running Test %s: ", __FUNCTION__);
int input_data[1] = {1};
int expected[1] = {1};
node * HEAD = createlinkedList(1, input_data);
printf("%s\n", check_LL(HEAD, 1, expected)?"PASSED":"FAILED");
}
void test_insert_after()
{
printf("Running Test %s: ", __FUNCTION__);
int input_data[2] = {1, 2};
int expected[2] = {1, 2};
node * HEAD = createlinkedList(2, input_data);
printf("%s\n", check_LL(HEAD, 2, expected)?"PASSED":"FAILED");
}
void test_insert_before()
{
printf("Running Test %s: ", __FUNCTION__);
int input_data[2] = {2, 1};
int expected[2] = {1, 2};
node * HEAD = createlinkedList(2, input_data);
printf("%s\n", check_LL(HEAD, 2, expected)?"PASSED":"FAILED");
}
/* We could leave test code in program and have a --test command line option to call the code */
int
main()
{
test_single_node();
test_insert_after();
test_insert_before();
}
node *
createlinkedList(int num, int * data)
{
int i;
node *head = NULL;
for (i = 0; i < num; i++) {
node * newNode = (node *) malloc(sizeof(node));
newNode->next = NULL;
newNode->data = data[i];
head = insertLLInOrder(head, newNode);
}
return head;
}
node *
insertLLInOrder(node * h, node * n)
{
//h is the head pointer, n is the pointer to new node
node *ptr = h;
node *previous = NULL;
while ((ptr != NULL) && (ptr->data < n->data)) {
previous = ptr; // remember previous node
ptr = ptr->next; // check for the next node
}
if (previous == NULL) {
//h is an empty list initially
n->next = NULL;
return n; // return the pointer of the new node
}
else {
//if there are nodes in the linked list
// previous will point to the node that has largest value, but smaller than new node
n->next = previous->next; // insert new node between previous, and previous->next
previous->next = n;
return h; // return old head pointer
}
}
As you can see the third test spot the bug.
Of course, you could use some available third party Unit Test library, but the most important point is not the test library, but to write the tests.
Another point is that really you should interleave writing tests and writing implementation code.
This typically helps for writing good code and is what people call TDD. But my answer is probably already long enough, so I won't elaborate here on TDD.
I am quite new to coding and got stuck with a problem. I tried to solve it myself and have been googling a lot, but I still do not have a solution to this. Maybe one of you can help?
This is my code:
int main(int argc, char **argv) {
struct node {
char *str;
int count;
struct node *next;
};
struct node head = { argv[1], 1, NULL };
for (int i = 2; i < (argc); i++) {
for (struct node *p = &head; (p != NULL); p = p->next) {
printf("%s,%s\n", argv[i], p->str);
if (strcmp(argv[i], p->str) == 0) {
printf("case1\n");
p->count++;
break;
}
else if ((strcmp(argv[i], p->str) != 0) && p->next) {
printf("case2\n");
printf("Adresse, auf die p zeigt: %p", &p);
continue;
}
else if ((strcmp(argv[i], p->str) != 0) && (!p->next)) {
printf("case3\n");
struct node *oldhead = &head;
head.str = argv[i];
head.count = 1;
head.next = oldhead;
break;
}
}
}
// Print how many times each string appears
return 0;
}
The goal is to create a linked list that contains all the arguments I gave to the main() when calling the program. If there is a duplicate, the structure should count them. For example, if i call the program like ./a.out foo fool foo the result should be a list of length two, where the first element contains the string "foo" and count 2, and the second element contains the string "fool" and has a count of 1. The problem is the else if-statement within the inner for loop. This is the only part where the inner for loop should actually be used and assign p->next to p. Unfortunately that is not happening. The result is that the inner for loop starts over and over again and the pointer p points to the same address all the time (I used printf to figure that out).
Does any of you have an idea what could be the problem here? I tried everything I could and tried to find a solution online ...
Thanks a lot!!!
The issue is in this part of the code
else if ((strcmp(argv[i], p->str) != 0) && (!p->next)) {
printf("case3\n");
struct node *oldhead = &head;
head.str = argv[i];
head.count = 1;
head.next = oldhead;
break;
}
You need to allocate a new struct and then put its address in the last struct entry.
else if ((strcmp(argv[i], p->str) != 0) && (!p->next)) {
printf("case3\n");
struct node *oldhead = p;
p = (struct node *) malloc(sizeof(node));
if (p == NULL) { .... manage the error ... }
oldhead->next = p;
p->str = argv[i];
p->count = 1;
p->next = NULL;
break;
}
Now you're creating nodes and stringing them together. You were effectively updating the same node before.
struct node *oldhead = &head;
head.str = argv[i];
head.count = 1;
head.next = oldhead;
That's not creating a new node. It's just creating a new reference to the same node, therefore causing an infinite loop when you try to read the linked list until the end. Therefore, your program only ever has one node. You need to actually allocate and create new ones.
The main problem here is
struct node *oldhead = &head;
which you should do malloc:
struct node *oldhead = (struct node*) malloc(sizeof(struct node));
so you really allocate a piece of memory for your new node. And because you have malloc, you should do free at the end of your program as well:
while(...) {
free(deepest_node)
}
the way you do the loop above is to go from the farthest node in the linked list all the way back to the head.
The other issue is that, you should not append your new node to head:
head.next = oldhead;
but should be to p, which is the last node in your linked list:
p -> next = oldhead;
i'm trying to invert the "info" field of the in a list like the one below
struct nodo {
int info;
struct nodo *next;
struct nodo *prev;
} ;
typedef struct nodo nodo;
Here is the main, the two output should be the original list of n mebmers, and the inverted list (First value go n , second n-1 and so on)
int main(int argc, const char * argv[]) {
struct nodo *p;
p = CreateList();
PrintList(p);
IvertList(p);
Printlist(p);
return 0;
}
Here is InvertList(): (Count() function just returns dimension of the list, i know it is a messy way but i'm focused on result for now)
void InvertList (struct nodo *p) {
int tmp = 0, num = 0, i = 0;
num = (Count(p));
tmp = num;
for (i=1; i!=tmp; i++) {
Swap(p,num);
num--;
}
}
And here is Swap(), this should bring a value (int info) to the first place of the list, to the last swapping with each:
void Swap (struct nodo *p, int n) {
int *tmp1 = NULL, *tmp2 = NULL;
int i;
for ( i = 1; i != n && p != NULL; i++) {
tmp1 = &p->info;
p = p->succ;
tmp2 = &p->info;
p->info = *tmp1;
p->prec->info = *tmp2;
}
}
Now the output i got printed is:
Value: 1
Value: 2
Value: 3
Value: 4
Value: 5
Value: 1
Value: 1
Value: 1
Value: 1
Value: 1
Where the last 5 values should be 5-4-3-2-1.
The bug(s) in your code not withstanding, you're not reversing your physical list at all, which I can all-but-guarantee is the point of the exercise in the first place.
Inversion of a linked list means all the pointers switch directions and the old tail becomes the new head. You seem to be avoiding that and trying to swap node info values instead.
To invert your list using simple pointer swapping:
// note head pointer passed by address
void InvertList(node **pp)
{
node *cur = *pp;
while (cur)
{
node *tmp = cur->prev;
cur->prev = cur->next;
cur->next = tmp;
*pp = cur;
cur = cur->prev;
}
}
And invoke from main() as:
InvertList(&p);
Note that no info values need be swapped, copied, etc. The node pointers simply switch direction and their enumeration will start at the other end. A full working example appears below:
#include<stdio.h>
#include<stdlib.h>
struct node
{
int info;
struct node *next;
struct node *prev;
};
typedef struct node node;
static void PrintList(const node *head)
{
while (head)
{
printf("%d: this=%p, prev=%p, next=%p\n",
head->info, head, head->prev, head->next);
head = head->next;
}
}
static void InvertList(node **pp)
{
node *cur = *pp;
while (cur)
{
node *tmp = cur->prev;
cur->prev = cur->next;
cur->next = tmp;
*pp = cur;
cur = cur->prev;
}
}
int main()
{
node *prev = NULL, *head = NULL, **pp = &head;
for (int i=1; i<=5; ++i)
{
*pp = malloc(sizeof **pp);
(*pp)->info = i;
(*pp)->prev = prev;
prev = *pp;
pp = &(*pp)->next;
}
*pp = NULL;
PrintList(head); // prints 1,2,3,4,5
InvertList(&head);
PrintList(head); // prints 5,4,3,2,1
}
Output (addresses vary, obviously)
1: this=0x1001054b0, prev=0x0, next=0x1001054d0
2: this=0x1001054d0, prev=0x1001054b0, next=0x1001054f0
3: this=0x1001054f0, prev=0x1001054d0, next=0x100105510
4: this=0x100105510, prev=0x1001054f0, next=0x100105530
5: this=0x100105530, prev=0x100105510, next=0x0
5: this=0x100105530, prev=0x0, next=0x100105510
4: this=0x100105510, prev=0x100105530, next=0x1001054f0
3: this=0x1001054f0, prev=0x100105510, next=0x1001054d0
2: this=0x1001054d0, prev=0x1001054f0, next=0x1001054b0
1: this=0x1001054b0, prev=0x1001054d0, next=0x0
There is a bug in your Swap fcn:
p->info = *tmp1;
p->prev->info = *tmp2;
But what are tmp1 and tmp2 at that time? Well, after we advanced p, tmp1 points at p->prev->info, while tmp2 = &p->info; So we could rewrite these assignments in effect as:
p->info = p->prev->info;
p->prev->info = p->info;
So, we could rewrite them again, in effect, as:
p->info = p->prev->info;
p->prev->info = p->prev->info;
So, the second assignment doesn't change anything in effect. Therefore, the first call to Swap in InvertList takes the value of the first element (1) and sets all the values in the list equal to it. The subsequent calls to Swap act similarly but have no effect as the list already contains all 1's.
Here's a simple way to rewrite Swap:
void Swap(struct nodo *p, int n)
{
if (n <= 1)
return;
int tmp = p->info;
for (int i = 1; i != n; ++i, p = p->next)
p->info = p->next->info;
p->info = tmp;
}
Note, however, that the way you've written InvertList is doing theta(n^2) work. The first loop iteration shifts the first element n-1 spots, the second iteration shifts the first element n-2 spots, the third iteration shifts the first element n-3 spots, and so on, down to shifting the first element 1 spot. So, you end up doing something like n * (n - 1) / 2 total shifts to reverse the list.
A linked list can be reversed / inverted in theta(n) work. See if you can figure out a better way to do this. If you had a pointer to both the beginning and the end of the list (which you often want anyway), then you could do something similar to reversing the characters in a string, for example.
I am trying basic creation of linked list using C. I have written the following code which is working up until first node but fails eventually on second one. I think the issue is where I am trying to display the node values in list separated by arrow(->). I think my logic is right but please correct me. Thanks in advance
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
struct node
{
int number;
struct node *next;
};
typedef struct node NODE;
NODE *node1, *node2, *start, *save;
int main()
{
node1 = (NODE *)malloc(sizeof(NODE));
int i = 0;
start = NULL;
for(i = 0; i < 3; i++)
{
int inf;
printf("Enter node value:");
scanf("%d", &inf);
node1->number = inf;
node1->next = NULL;
if(start == NULL)
{
start = node1;
save = node1;
}
else
{
// save=start;
// start=node1;
// node1->next=save;
node1->next = start;
start = node1;
}
while(node1 != NULL)
{
printf("%d ->",node1->number);
node1 = node1->next;
}
}
return 0;
}
The issues are
How you're allocating your nodes for insertion (i.e. save for one, you're not).
How they're placed in the list once you fix the above.
Don't cast malloc in C programs (read here for why).
Fail to check the success of your scanf invoke.
Fail to check the success of your malloc invoke
Before you get discouraged, things you did correctly:
Did not mask a node pointer in a typedef
Properly included a MCVE for review
Prospected the things you may be doing wrong.
A very simple example of iterating three values into a linked list would look something like this:
#include <stdio.h>
#include <stdlib.h>
struct node
{
int number;
struct node *next;
};
typedef struct node NODE;
int main()
{
NODE *head = NULL, *p;
int i = 0;
for(i = 0; i < 3; i++)
{
int inf;
printf("Enter node value:");
if (scanf("%d", &inf) == 1)
{
p = malloc(sizeof *p);
if (p != NULL)
{
p->number = inf;
p->next = head;
head = p;
}
else
{
perror("Failed to allocate new node");
return EXIT_FAILURE;
}
}
else
{
// failed to read data. break
break;
}
// report current linked list
printf("%d", p->number);
for (p=p->next; p; p = p->next)
printf(" -> %d", p->number);
fputc('\n', stdout);
}
// cleanup the linked list
while (head)
{
p = head;
head = head->next;
free(p);
}
head = NULL;
return 0;
}
Input
The values 1 2 3 are input upon being prompted:
Output
Enter node value:1
1
Enter node value:2
2 -> 1
Enter node value:3
3 -> 2 -> 1
Best of luck.
You should use malloc() inside for loop.
Since it is outside, same memory is being used.
As said by Vamsi, you should use malloc to put the nodes on the heap. You also generally shouldn't cast the output of malloc, it isn't needed. And then you could play around with making a doubly-linked list, where you also have a prev pointer inside your struct.