how to copy strings into a linked list?

J

Jim Showalter

I'm trying to write code that gets fixed-length strings from the user
and then stores them in a linked list.

Here's the definition of my list node:

struct node {char str[19]; struct node* next; };

And here is my attempt at Push(), the function that adds new nodes
to the list:

void Push(struct node** head, char *str) {

struct node* newNode = malloc(sizeof(struct node));
newNode->str = *str;
newNode->next = *head;
*head = newNode;
}

It worked fine when testing with int's, but I don't know how to get the
strings copied to "newNode->str".
 
W

Walter Roberson

I'm trying to write code that gets fixed-length strings from the user
and then stores them in a linked list.
Here's the definition of my list node:
struct node {char str[19]; struct node* next; };
And here is my attempt at Push(), the function that adds new nodes
to the list:
void Push(struct node** head, char *str) {
struct node* newNode = malloc(sizeof(struct node));

You should check the result of the malloc() -- if there was no
available memory then malloc() will return a NULL pointer.
newNode->str = *str;
newNode->next = *head;
*head = newNode;
}
It worked fine when testing with int's, but I don't know how to get the
strings copied to "newNode->str".

strncpy( &newNode->str, str, sizeof(newNode->str) - 1 );
newNode->str[sizeof newNode->str - 1] = '\0';

But this code depends upon you really wanting strings -- which is
to say, null terminated character arrays. You said that you
have "fixed-length strings": does that mean that there are 18
used characters and the 19th is for the null, or does that mean
that there are 19 used characters and you will supply the null
when needed? The code as written assumes that the null needs
to be stored and protects itself in case the null was not present
in the first 18 characters of the input.
 
J

Jim Showalter

Walter said:
You should check the result of the malloc() -- if there was no
available memory then malloc() will return a NULL pointer.

Thanks - I will when I get this function working.
strncpy( &newNode->str, str, sizeof(newNode->str) - 1 );
newNode->str[sizeof newNode->str - 1] = '\0';

I think you're close, but now I'm getting:
"warning: passing argument 1 of ‘strncpy’ from incompatible pointer type"
.... does that mean that there are 18
used characters and the 19th is for the null, ...

Yes
 
W

Walter Roberson

Walter Roberson wrote:

In that case, you can use strcpy() instead of strncpy(), and
you can skip the statement that sets the final character to '\0'.
However, it is -safer- to use strncpy() and set the '\0'
as that way a small mistake somewhere else in constructing the
string will not end up potentially trashing random bits of memory.
I think you're close, but now I'm getting:
"warning: passing argument 1 of ‘strncpy’ from incompatible pointer type"

Ugly, why doesn't the warning message just stick to the basic
character set instead of moving into UTF-8 extensions for whatever
kind of quotation marks it is using around strncpy ?

Anyhow, better than &newNode->str would be either
newnode->str without the &, or else &newNode->str[0]
 
P

pete

Jim said:
I'm trying to write code that gets fixed-length strings from the user
and then stores them in a linked list.

In line_to_string.c, string_node is the function
which copies strings into a linked list.

/* BEGIN line_to_string.c */

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>

struct list_node {
struct list_node *next;
void *data;
};

int line_to_string(FILE *fp, char **line, size_t *size);
void list_free(struct list_node *node, void (*free_data)(void *));
void list_fprint(FILE *stream, struct list_node *node);
struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data);

int main(void)
{
struct list_node *head, *tail;
int rc;
char *buff_ptr;
size_t buff_size;
long unsigned line_count;

puts(
"\nThis program makes and prints a list of all the lines\n"
"of text entered from the standard input stream.\n"
"Just hit the Enter key to end,\n"
"or enter any line of characters to continue."
);
tail = head = NULL;
line_count = 0;
buff_size = 0;
buff_ptr = NULL;
while ((rc = line_to_string(stdin, &buff_ptr, &buff_size)) > 1) {
++line_count;
tail = string_node(&head, tail, buff_ptr);
if (tail == NULL) {
break;
}
puts(
"\nJust hit the Enter key to end,\n"
"or enter any other line of characters to continue."
);
}
switch (rc) {
case EOF:
if (buff_ptr != NULL && strlen(buff_ptr) > 0) {
puts("rc equals EOF\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = string_node(&head, tail, buff_ptr);
}
break;
case 0:
puts("realloc returned a null pointer value");
if (buff_size > 1) {
puts("rc equals 0\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = string_node(&head, tail, buff_ptr);
}
break;
default:
break;
}
if (line_count != 0 && tail == NULL) {
puts("Node allocation failed.");
puts("The last line entered didnt't make it onto the list:");
puts(buff_ptr);
}
free(buff_ptr);
puts("\nThe line buffer has been freed.\n");
printf("%lu lines of text were entered.\n", line_count);
puts("They are:\n");
list_fprint(stdout, head);
list_free(head, free);
puts("\nThe list has been freed.\n");
return 0;
}

int line_to_string(FILE *fp, char **line, size_t *size)
{
int rc;
void *p;
size_t count;

count = 0;
while ((rc = getc(fp)) != EOF) {
++count;
if (count + 2 > *size) {
p = realloc(*line, count + 2);
if (p == NULL) {
if (*size > count) {
(*line)[count] = '\0';
(*line)[count - 1] = (char)rc;
} else {
ungetc(rc, fp);
}
count = 0;
break;
}
*line = p;
*size = count + 2;
}
if (rc == '\n') {
(*line)[count - 1] = '\0';
break;
}
(*line)[count - 1] = (char)rc;
}
if (rc != EOF) {
rc = count > INT_MAX ? INT_MAX : count;
} else {
if (*size > count) {
(*line)[count] = '\0';
}
}
return rc;
}

void list_free(struct list_node *node, void (*free_data)(void *))
{
struct list_node *next_node;

while (node != NULL) {
next_node = node -> next;
free_data(node -> data);
free(node);
node = next_node;
}
}

void list_fprint(FILE *stream, struct list_node *node)
{
while (node != NULL) {
fputs(node -> data, stream);
putc('\n', stream);
node = node -> next;
}
}

struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data)
{
struct list_node *node;

node = malloc(sizeof *node);
if (node != NULL) {
node -> next = NULL;
node -> data = malloc(strlen(data) + 1);
if (node -> data != NULL) {
if (*head == NULL) {
*head = node;
} else {
tail -> next = node;
}
strcpy(node -> data, data);
} else {
free(node);
node = NULL;
}
}
return node;
}

/* END line_to_string.c */
 
J

Jim Showalter

Walter said:
In that case, you can use strcpy() instead of strncpy(), and
you can skip the statement that sets the final character to '\0'.
However, it is -safer- to use strncpy() and set the '\0'
as that way a small mistake somewhere else in constructing the
string will not end up potentially trashing random bits of memory.

Thanks for the tip. I may stick with strncpy() as you suggest.
Anyhow, better than &newNode->str would be either
newnode->str without the &, or else &newNode->str[0]

newnode->str works. Thank you!
 
J

Jim Showalter

pete said:
In line_to_string.c, string_node is the function
which copies strings into a linked list.

/* BEGIN line_to_string.c */

Wow - thanks for the routines! And it all compiled without a whimper. That
should get me over this particular hurdle.
/* END line_to_string.c */

Thanks again, pete!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top