Linked List problem.

B

Ben

Hi,

I am a newbie with C and am trying to get a simple linked list working
for my program. The structure of each linked list stores the char
*data and *next referencing to the next link. The problem I get is
that I am trying to link a struct that I have defined and its refusing
to link. I have tried casting my struct into char * but attempts to
cast it back to its original struct to access its contents only seg
faults.

I'd attempt to change the type of data in the linked list to that my
struct but that just doesnt seem right.
Any advice is appreciated.

Thanks
Ben
 
M

Mike Wahler

Ben said:
Hi,

I am a newbie with C and am trying to get a simple linked list working
for my program. The structure of each linked list stores the char
*data and *next referencing to the next link. The problem I get is
that I am trying to link a struct that I have defined and its refusing
to link. I have tried casting my struct into char * but attempts to
cast it back to its original struct to access its contents only seg
faults.

I'd attempt to change the type of data in the linked list to that my
struct but that just doesnt seem right.
Any advice is appreciated.

Your bug is on line 42.

-Mike
 
T

Tim Rentsch

I am a newbie with C and am trying to get a simple linked list working
for my program. The structure of each linked list stores the char
*data and *next referencing to the next link. The problem I get is
that I am trying to link a struct that I have defined and its refusing
to link. I have tried casting my struct into char * but attempts to
cast it back to its original struct to access its contents only seg
faults.

I'd attempt to change the type of data in the linked list to that my
struct but that just doesnt seem right.
Any advice is appreciated.


First, don't do any casting. Casting is unnecessary for coding a
simple linked list, and if casting is unnecessary it usually is best
to avoid it.


Second, start with defining the type, perhaps something like this:

typedef struct node_struct_tag *List, ListElement;

struct node_struct_tag {
char *data;
List next;
};


Now, a simple function:

unsigned int
list_length( List head_of_list ){
List remainder; unsigned int result;

remainder = head_of_list;
result = 0;

while( remainder != 0 ){
result++;
remainder = remainder->next;
}

return result;
}


A function for list formation:

List
lisp_style_cons( char *value, List tail ){
List result;

result = malloc( sizeof( *result ) );
assert( result != 0 );

result->data = value;
result->next = tail;

return result;
}


A function for more typical C-style manipulation:

void
splice_one_element_into_list( List element, List *list_address ){
assert( list_address != 0 );
assert( element != 0 );

element->next = *list_address;
*list_address = element;
}


That function might be used in a function that inserts into
a sorted list:

void
insert_into_sorted_list( List element, List *sorted_list_address ){
List *insertion_point;

assert( element != 0 );
assert( sorted_list_address != 0 );

insertion_point = sorted_list_address;
while( *insertion_point != 0 ){
if( strcmp( (*insertion_point)->data, element->data ) > 0 ){
break;
}
insertion_point = & (*insertion_point)->next;
}

splice_one_element_into_list( element, insertion_point );
}


Before trying to compile this code, read through it and make sure
you understand what you think it's doing and why.

If you want to try compiling, first find the appropriate headers for
assert(), malloc() and strcmp(), and #include them.

The functions above almost certainly aren't what you want, but if
you can understand these you should be able to write the functions
that you do want.

Disclaimer: the code above has not been compiled or even syntax
checked (except manually by myself). I'm confident the many
readers of the NG will graciously report any bonehead errors
I've made. :)
 
A

Al Bowers

Tim said:
typedef struct node_struct_tag *List, ListElement;

struct node_struct_tag {
char *data;
List next;
};


....snip....

List
lisp_style_cons( char *value, List tail ){
List result;

result = malloc( sizeof( *result ) );
assert( result != 0 );

result->data = value;
result->next = tail;

return result;
}

....snip...


Disclaimer: the code above has not been compiled or even syntax
checked (except manually by myself). I'm confident the many
readers of the NG will graciously report any bonehead errors
I've made. :)

I haven't noticed in bonehead errors but function lisp_style_cons,
listed above, scares me. The function requires that 'value'
point to storage allocated somewhere else. I prefer to encapsulate
the allocation and assignment in the function that creates the node.
Examples below:

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

typedef struct NODE
{
char *data;
struct NODE *next;
} NODE;

unsigned NodesInList( NODE *p );
int AddListHead( NODE **p, const char *s );
int AddListTail( NODE **p, const char *s );
void PrintList( NODE *p );
void FreeList( NODE **p );

int main(void)
{
NODE *head = NULL;

AddListTail(&head,"Hello World");
AddListTail(&head,"Goodbye World");
AddListHead(&head,"The list head");
printf("There are %u Nodes in the list.\n",
NodesInList(head));
PrintList(head);
FreeList(&head);
printf("\nFreed the list. Now there are %u "
"nodes in the list.\n",NodesInList(head));
return 0;
}

unsigned NodesInList( NODE *p )
{
unsigned nelem;

for(nelem = 0; p; p = p->next,nelem++) ;
return nelem;
}

int AddListHead( NODE **p, const char *s )
{
NODE *new;

if((new = malloc(sizeof *new)) == NULL) return 0;
if((new->data = malloc(strlen(s)+1)) == NULL)
{
free(new);
return 0;
}
strcpy(new->data,s);
new->next = *p;
*p = new;
return 1;
}

int AddListTail( NODE **p, const char *s )
{
NODE **tmp, *new;

if((new = malloc(sizeof *new)) == NULL) return 0;
if((new->data = malloc(strlen(s)+1)) == NULL)
{
free(new);
return 0;
}
strcpy(new->data,s);
new->next = NULL;
for(tmp = p; *tmp != NULL;tmp = &(*tmp)->next) ;
*tmp= new;
return 1;
}

void PrintList( NODE *p )
{
unsigned i;

for(i = 1 ; p; p = p->next,i++)
printf("Node %u: Data is \"%s\"\n",i,p->data);
return;
}

void FreeList( NODE **p )
{
NODE *tmp;

for( ; *p; *p = tmp)
{
tmp = (*p)->next;
free((*p)->data);
free(*p);
}
return;
}
 
B

Ben

Hello,

Dont quite understand how your code fixes my query.
I already have a working, linkedlist.c. I know it works because I have
tested it using strings as data.
My linkedlist has the following struct

struct link {
char *data
link *next
}
#excluding respective typedef statements

#enqueue adds a new element to list
enqueue(*link, char *data)

In my main.c program.
I have another structure which i want to put into a linked list

struct people {
char *name
int *age
char *address
}

Now I have included my linkedlist.c into my main.c
However I cant do enqueue(peoplelist, *newpeople)
where peoplelist is a *link and *newpeople is a people
because gcc complains that it wants char instead of people

and enqueue(peoplelist, (char *) newpeople) only gives me
*name when i try to access the element again (because of the string
terminator?). It also wont let me cast it back to (people) and wont
let me access within newpeople (eg newpeople.age).

I was wondering if there was a way to allow linkedlist to use people
as an element but without changing the struct link's char *data to
people *data
Is this possible or is there another better solution?
 
A

Al Bowers

Ben said:
Hello,

Dont quite understand how your code fixes my query.
I already have a working, linkedlist.c. I know it works because I have
tested it using strings as data.
My linkedlist has the following struct

struct link {
char *data
link *next
}
#excluding respective typedef statements

#enqueue adds a new element to list
enqueue(*link, char *data)

This doesn't look right. You need to provide the definition
of function enqueue plus code on how you use it. Provide actual
code, not pseudo code.
In my main.c program.
I have another structure which i want to put into a linked list

struct people {
char *name
int *age
char *address
}

Now I have included my linkedlist.c into my main.c
However I cant do enqueue(peoplelist, *newpeople)
where peoplelist is a *link and *newpeople is a people
because gcc complains that it wants char instead of people

and enqueue(peoplelist, (char *) newpeople) only gives me
*name when i try to access the element again (because of the string
terminator?). It also wont let me cast it back to (people) and wont
let me access within newpeople (eg newpeople.age).

It will not let you because it is erroneous.
I was wondering if there was a way to allow linkedlist to use people
as an element but without changing the struct link's char *data to
people *data
Is this possible or is there another better solution?

The solution requires you to change your struct link.

Perhaps:

struct link
{
struct people data;
struct link *next;
};

or
struct link
{
struct people *data;
struct link *next;
};

Then rewrite function enqueue and other functions that manipulates
the link list. A simple cast will not work.

Example:

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

struct people
{
char *name;
unsigned age;
char *address;
};

typedef struct NODE
{
struct people data;
struct NODE *next;
} NODE;

unsigned NodesInList( NODE *p );
int enqueue( NODE **p, const char *name, unsigned age,
const char *addr );
void PrintList( NODE *p );
void FreeList( NODE **p );

int main(void)
{
NODE *head = NULL;

enqueue(&head,"George Washington",46,
"2000 Adams Blvd\nWashington DC");
enqueue(&head,"Bill Clinton", 43,
"152 E. Hickory Ave\nLittle Rock, AR");
enqueue(&head,"George Bush",37,
"1401 Vine St\nHouston, TX");
printf("There are %u Nodes in the list.\n\n",
NodesInList(head));
PrintList(head);
FreeList(&head);
printf("\nFreed the list. Now there are %u "
"nodes in the list.\n",NodesInList(head));
return 0;
}

unsigned NodesInList( NODE *p )
{
unsigned nelem;

for(nelem = 0; p; p = p->next,nelem++) ;
return nelem;
}

int enqueue( NODE **p, const char *name, unsigned age,
const char *addr )
{
NODE **tmp, *new;

if((new = malloc(sizeof *new)) == NULL) return 0;
if((new->data.name = malloc(strlen(name)+1)) == NULL)
{
free(new);
return 0;
}
if((new->data.address = malloc(strlen(addr)+1)) == NULL)
{
free(new->data.name);
free(new);
return 0;
}
strcpy(new->data.name, name);
strcpy(new->data.address, addr);
new->data.age = age;
new->next = NULL;
for(tmp = p; *tmp != NULL;tmp = &(*tmp)->next) ;
*tmp= new;
return 1;
}

void PrintList( NODE *p )
{
unsigned i;

for(i = 1 ; p; p = p->next,i++)
printf("Node %u:\n%s Age %u\n%s\n\n",i,p->data.name,
p->data.age, p->data.address);
return;
}

void FreeList( NODE **p )
{
NODE *tmp;

for( ; *p; *p = tmp)
{
tmp = (*p)->next;
free((*p)->data.name);
free((*p)->data.address);
free(*p);
}
return;
}
 
T

Tim Rentsch

Al Bowers said:
Tim Rentsch wrote:
[some cutting here]
List
lisp_style_cons( char *value, List tail ){
List result;

result = malloc( sizeof( *result ) );
assert( result != 0 );

result->data = value;
result->next = tail;

return result;
}

[some cutting here]

I haven't noticed in bonehead errors but function lisp_style_cons,
listed above, scares me. The function requires that 'value'
point to storage allocated somewhere else. I prefer to encapsulate
the allocation and assignment in the function that creates the node.
[Example code omitted]

I take your point. What is the "right" way to do storage allocation
in C is a non-trivial topic, and the pattern shown above certainly
isn't typical. I thought it would help illustrate a linked-list
function that the person asking the question might be familiar with
and so be helpful in that way. The storage allocations issues were
definitely glossed over in my posting, so as not to overcomplicate the
example.
 
T

Tim Rentsch

Hello,

Dont quite understand how your code fixes my query.
I already have a working, linkedlist.c. I know it works because I have
tested it using strings as data.
My linkedlist has the following struct

struct link {
char *data
link *next
}
#excluding respective typedef statements

#enqueue adds a new element to list
enqueue(*link, char *data)

In my main.c program.
I have another structure which i want to put into a linked list

struct people {
char *name
int *age
char *address
}

Now I have included my linkedlist.c into my main.c
However I cant do enqueue(peoplelist, *newpeople)
where peoplelist is a *link and *newpeople is a people
because gcc complains that it wants char instead of people

and enqueue(peoplelist, (char *) newpeople) only gives me
*name when i try to access the element again (because of the string
terminator?). It also wont let me cast it back to (people) and wont
let me access within newpeople (eg newpeople.age).

I was wondering if there was a way to allow linkedlist to use people
as an element but without changing the struct link's char *data to
people *data
Is this possible or is there another better solution?

If you've been reading comp.lang.c for a while then you know that
people with questions are told to post actual code rather than
summaries or descriptions. And, if the actual code is lots of lines,
to prepare an example as reasonably small as they can that still
demonstrates the problem they want to ask about.

There are various reasons for doing this, some having to do with
courtesy and politeness, some having to do with NG readers being able
to actually supply an answer, and some having to do with maximizing
*your* value from any answers you might get.

A. Working on preparing the posting might let you see the answer
yourself. That's more valuable to you than if it has to be explained.

B. "[gcc] won't let me cast it back." The problem with this statement
is almost everyone reading it won't know what you mean. Of course,
the few readers of CLC who are true mindreaders will, but they
generally don't like posting answers. [:)]

C. I appreciate that you probably think you're making it easier on
people by posting only a short description rather than some long code
body, etc. Most readers, though, are frustrated because they don't
have enough information to answer the question, and it seems obvious
to them that there isn't enough information to answer the question.
So, to really make it easier for the readers, put in the time to get
*real code* that illustrates the problem in as short a space as
you can.

Based on what you wrote, it seems clear that you're confused
about _something_ related to casting and/or pointers; beyond
that, I'm not going to guess.

Oh, one more thing - I think I'd be remiss if I didn't put in
a reminder to read the comp.lang.c FAQ for starters. I'm sure
these points must be covered in there somewhere.
 
M

Mike Deskevich

send the code you're trying to run (or better - a simplified version
that exhibits the problems you're having). it's impossible to help
unless we know where you're coming from.
 
B

Ben

Mike Deskevich said:
send the code you're trying to run (or better - a simplified version
that exhibits the problems you're having). it's impossible to help
unless we know where you're coming from.

Thanks to all who have been advising and sorry for the long overdue reply.
My question is all about casting and pointers and what conditions allow it.

Please correct my following assumptions,

1) Int can be casted into a char
int i=0;
char a;
a=(char *) i;


2) Structure casted into an array of char
typedef struct {
char name[20];
int age;
int id;
} person;

person p = (person *) malloc(sizeof(person));
p.name="hello";
p.age=2;
p.id=2;

char *a;
a=(char *)p;
 
R

Richard Bos

Please correct my following assumptions,

1) Int can be casted into a char
int i=0;
char a;
a=(char *) i;

This is confused. _Either_ you assign an int to a char, in which case
you don't need the cast (and the cast as written is wrong), _or_ you
assign it to a char * (and the declaration of a is wrong), in which
case:
Yes, you can, but the result is not necessarily meaningful. It may not
be a valid pointer at all; it may not point to memory you can write to
or even read; and even when it does, it may not point where you expect
it to point. In particular, in the above example, a need _not_ be a null
pointer.
At least a char * is always correctly aligned. Had a been an int *, for
example, it might have been an incorrectly aligned pointer.
2) Structure casted into an array of char
typedef struct {
char name[20];
int age;
int id;
} person;

person p = (person *) malloc(sizeof(person));
p.name="hello";
p.age=2;
p.id=2;

char *a;
a=(char *)p;

No. You cannot cast a struct into a pointer. And what would you do this
for, anyway? Do you suppose that the bytes represented by "hello"
somehow form a valid pointer?
I suspect you meant to access the struct itself _through_, not _as_, a
pointer to char. In that case, you need to do

a = (char *)&p;

which is perfectly legal, and can be useful - though rarely. Beware the
padding bytes!

Richard
 

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

Forum statistics

Threads
474,148
Messages
2,570,838
Members
47,385
Latest member
Joneswilliam01

Latest Threads

Top