Reputation: 1100
I am writing a linked list as part of a larger project, and have run into an issue. This testcase takes a string of comma delimited ranges (where a range in this case is either an integer or two integers separated by a dash), and add each range into a singly linked list. Currently, it's only printing the first two ranges, and I can't see why. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char port_list[] = "22-25,80,443-445,4200-4205";
struct range_list {
struct range_list *next;
char *range;
};
struct range_list *head = (struct range_list*) malloc(sizeof(struct range_list));
head->next = 0;
head->range = strtok(port_list, ",");
struct range_list *iter = (struct range_list*) malloc(sizeof(struct range_list));
head->next = iter;
iter->next = 0;
while((iter->range = strtok(NULL, ",")) != NULL) {
iter = (struct range_list*) malloc(sizeof(struct range_list));
iter->next = iter;
iter->next = 0;
}
for(iter=head; iter != 0; iter=iter->next) {
printf("%s\n", iter->range);
}
}
Currently, the output is:
22-25
80
And ideally I would like:
22-25
80
443-445
4200-4205
Thanks for any help in advance! I'm sure it's a simple issue. I just get scared writing linked lists, so am very out of practice.
Upvotes: 0
Views: 50
Reputation: 2790
Here you are overwriting iter:
while((iter->range = strtok(NULL, ",")) != NULL) {
iter = (struct range_list*) malloc(sizeof(struct range_list)); // you are overwriting iter;
iter->next = iter;
iter->next = 0;
}
Try this:
struct range_list* tmp;
struct range_list* last;
while((iter->range = strtok(NULL, ",")) != NULL) {
last = iter;
tmp = (struct range_list*) malloc(sizeof(struct range_list));
iter->next = tmp;
iter = iter->next;
iter->next = 0;
}
free(last->next);
last->next = 0;
for(iter=head; iter != 0; iter=iter->next) {
printf("%s\n", iter->range);
}
Upvotes: 0
Reputation: 2456
This seems to work. I think it's basically the same everyone is saying in different words.
char port_list[] = "22-25,80,443-445,4200-4205";
char *range;
// Create the head and start the tokener.
struct range_list *iter, *head = calloc(1, sizeof(struct range_list));
head->range = strtok(port_list, ",");
// As long as there is a token append a new item.
while((range = strtok(NULL, ",")) != NULL) {
iter = (struct range_list*) malloc(sizeof(struct range_list));
iter->next = head;
iter->range = range;
head = iter;
}
for(iter=head; iter != 0; iter=iter->next) {
printf("%s\n", iter->range);
}
OUTPUT
4200-4205
443-445
80
22-25
Upvotes: 0
Reputation: 73627
Your code has an issue if there's only one range in the string.
I'll therefore propose you a cleaner alternative:
...
// as before, assuming ther's at least one range
struct range_list *head = (struct range_list*) malloc(sizeof(struct range_list));
head->next = 0;
head->range = strtok(port_list, ",");
struct range_list *iter=head, *tmp;
char *s;
while((s = strtok(NULL, ",")) != NULL) {
// there is a new item. So first create and initialise it
tmp = (struct range_list*) malloc(sizeof(struct range_list));
tmp->next=0;
tmp->range=s;
// then link it to the previous and interate
iter->next = tmp;
iter = iter->next;
}
...
This one assumes that there is at least one range in the string to be put into head
.
Note that the range
pointers point to the original buffer. Here it's not a problem, because the original buffer remains unchanged. You may consider strdup()
if you're on a posix system to make a safe copy of your range string if there's a risk that your linked list lives longer than the buffer or its content.
Upvotes: 2