By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
425,749 Members | 1,627 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 425,749 IT Pros & Developers. It's quick & easy.

Problem with a linked list

P: n/a
Hi,

I am writing a linked list in the following way.

struct list
{
struct list *next;
char *mybuff;
};

struct list *myList = NULL;
struct list *endList = NULL;

void getline(char s[], int lim)
{
int c, i;

for(i=0; ((i<lim-1) && ((c=getchar()) != '\n')); i++)
s[i] = c;

s[i] ='\0';
} // end method getline

void add_item(char *data)
{
if (!endList)
{
endList = (struct list *)malloc(sizeof(struct list));

myList = endList;
endList->mybuff = data;
endList->next = NULL;
} // end if
else
{
endList->next = (struct list *)malloc(sizeof(struct list));
endList = endList>next;
endList->mybuff = data;
endList->next = NULL;
} // end else
}

void printList()
{
struct list *current = myList;

while(current)
{
printf("%s\n", current->mybuff);
current = current->next;
}
}

int main()
{
char buff[50];

// called for a n times
getline(buff, 50);
add_item(buff);

printList();
}

Now in the printList method, the nothing is being printed, but just a
simple blank line for each item.

Can someone help me solve this problem out.
Thanks in Advance
Nov 14 '05 #1
Share this Question
Share on Google+
57 Replies


P: n/a
be*********@yahoo.com (Xarky) writes:
int main()
{
char buff[50];

// called for a n times
getline(buff, 50);
add_item(buff);

printList();
}

Now in the printList method, the nothing is being printed, but just a
simple blank line for each item.


Of course. What you have is not a list of strings, but a list of
pointers to buff, so printList() just prints N copies of whatever it
was you typed on line N.

DES
--
Dag-Erling Smørgrav - de*@des.no
Nov 14 '05 #2

P: n/a
Are you sure this program compiled, and that you are not looking
at some other binary? There is a syntax error at the line:
endList = endList>next; (should be endList->next)

Besides this, have you realized that you are 'assigning' data
without allocating any memory to the structure element mybuff?
This way you might end up corrupting many other things, however,
sometimes, you would find the same string (mybuff) in all the
nodes.

Overall, a poorly written code.

Nov 14 '05 #3

P: n/a
voke, what i think is u have not allocated memory fo the char * mybuff
member of the structure and what it will point is completely
implementation dependent......if the same char * is replaced with int or
plain char it will definitely work, provided u have the right syntax and
logic.

Nov 14 '05 #4

P: n/a

be*********@yahoo.com (Xarky) writes:
int main()
{
char buff[50];

// called for a n times
getline(buff, 50);
add_item(buff);

printList();
}

Now in the printList method, the nothing is being printed, but just a
simple blank line for each item.


Of course. What you have is not a list of strings, but a list of
pointers to buff, so printList() just prints N copies of whatever it
was you typed on line N.

DES


Use a library for your linked lists if possible. glib from www.gtk.org was
pointed out to me on this ng and is what I'll be using in any future C
programming projects.
Martin
Nov 14 '05 #5

P: n/a
Xarky wrote:

I am writing a linked list in the following way.
.... snip ...
Now in the printList method, the nothing is being printed, but
just a simple blank line for each item.

Can someone help me solve this problem out.


C doesn't have methods, but it does have functions. Try the
following and understand why it is written as it is.

/* A simplified demo of creating a linked list */
/* EOF or a non-numeric entry ends entry phase */

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

typedef int datatype;

struct node {
struct node *next;
datatype datum;
};

/* ----------------- */

/* add value to head of list */
struct node *addtolist(struct node *root, datatype value)
{
struct node *newnode;

if ((newnode = malloc(sizeof *newnode))) {
newnode->next = root;
newnode->datum = value;
}
return newnode;
} /* addtolist */

/* ----------------- */

void showlist(struct node *root)
{
while (root) {
printf("%d\n", root->datum);
root = root->next;
}
} /* showlist */

/* ----------------- */

/* believed necessary and sufficient for NULL terminations */
/* Reverse a singly linked list. Reentrant (pure) code */
struct node *revlist(struct node *root)
{
struct node *curr, *nxt;

if (root) { /* non-empty list */
curr = root->next;
root->next = NULL; /* terminate new list */
while (curr) {
nxt = curr->next; /* save for walk */
curr->next = root; /* relink */
root = curr; /* save for next relink */
curr = nxt; /* walk onward */
}
}
/* else empty list is its own reverse; */
return root;
} /* revlist */

/* ----------------- */

/* return all the storage for a list */
struct node *killlist(struct node *root)
{
struct node *tmp;

while ((tmp = root)) {
root = root->next;
free(tmp);
}
return root;
} /* killlist */

/* ----------------- */

int main(void)
{
struct node *root, *tmp;
datatype num;

root = NULL;
puts("Entry until non numeric value:");
while (1 == scanf("%d", &num)) {
if ((tmp = addtolist(root, num))) root = tmp;
else break;
}
puts("Entries, 1 per line, from last:");
showlist(root);
root = revlist(root);
puts("Same list, reversed:");
showlist(root);
root = killlist(root);
return 0;
} /* main linklist.c */

/* A run of the above code:
Entry until non numeric value:
1 2 40 3 5
8 9 x
Entries, 1 per line, from last:
9
8
5
3
40
2
1
Same list, reversed:
1
2
40
3
5
8
9
*/

--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!

Nov 14 '05 #6

P: n/a
Xarky wrote:
I am writing a linked list in the following way.

struct list
{
struct list *next;
char *mybuff;
};

struct list *myList = NULL;
struct list *endList = NULL;

void getline(char s[], int lim)
{
int c, i;

for(i=0; ((i<lim-1) && ((c=getchar()) != '\n')); i++)
s[i] = c;

s[i] ='\0';
} // end method getline


"//" is not a portable way of expressing a comment in C. Not that
portability is an issue in this newsgroup. (You can also use fgets()
as an alternative to the function you've written.)

Ok, to the issue of linked lists.

1. First of all, you have to remember to allocate individual storage
for each string that you input. If you reuse the same buffer for
input, then your older input will simply be overwritten by it each
time.

2. Ok, there is also a common obfuscation that people do when they make
linked lists of distinguishing the "empty list" case from the other
cases. Actually both cases are the same if you think of the "current
tail pointer" as a reference to the link point, rather than the upper
container of the link point. I.e., tail should be &("last"->next)
rather than just "last". In this way the "current tail pointer" for
empty list is just the address of the top-of-list pointer, and is just
the address of the last "->next" record in the list.

I've demonstrated this in the code below:

static char * copystr (const char * data) {
int l = strlen(data) + 1;
char * s = (char *) malloc (sizeof (char) * l);
if (s) memcpy (s, data, l);
return s;
}

struct list ** add_item (struct list ** tail, char * data) {
if (NULL == tail ||
NULL != *tail ||
(NULL == (*tail = (struct list *) malloc(sizeof(struct list))))
)
return NULL;
(*tail)->mybuff = copystr (data);
(*tail)->next = NULL;
return &((*tail)->next);
}

void printlist (struct list * head) {
while (head) {
printf ("%s\n", head->mybuff);
head = head->next;
}
}

void destroylist (struct list ** tail) {
if (NULL == tail) return;
while (*tail) {
struct list ** next = &((*tail)->next);
(*tail)->next = NULL;
free ((*tail)->mybuff);
free (*tail);
tail = next;
}
}

int main () {
int i;
char buff[51];
struct list * top = NULL, * cur, ** tailptr;

for (tailptr = &top, i=0; i < 10; i++) {
getline (buff, 50);
tailptr = add_item (tailptr, buff);
}

printlist (top);
destroylist (&top);
return 0;
}

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #7

P: n/a
we******@gmail.com writes:
[...]
"//" is not a portable way of expressing a comment in C.
True.
Not that
portability is an issue in this newsgroup.


What???

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #8

P: n/a
we******@gmail.com wrote:
Xarky wrote:
I am writing a linked list in the following way.
.... snip ...
"//" is not a portable way of expressing a comment in C. Not that
portability is an issue in this newsgroup. (You can also use
fgets() as an alternative to the function you've written.)
On the contrary, it is extremely important. The point about // is
that it is only portably usable on C99 compliant systems, and that
it is extremely vulnerable to linewrap problems in usenet postings.

.... snip ...
2. Ok, there is also a common obfuscation that people do when they
make linked lists of distinguishing the "empty list" case from the
other cases. Actually both cases are the same if you think of the
"current tail pointer" as a reference to the link point, rather
than the upper container of the link point. I.e., tail should be
&("last"->next) rather than just "last". In this way the "current
tail pointer" for empty list is just the address of the top-of-list
pointer, and is just the address of the last "->next" record in the
list.
As I demonstrated in an earlier posting in this thread, there is no
need for the complication of maintaining a tail pointer.

I've demonstrated this in the code below:

static char * copystr (const char * data) {
int l = strlen(data) + 1;
char * s = (char *) malloc (sizeof (char) * l);
The cast is superfluous, and only serves to hide the error of
failing to #include <stdlib.h>. sizeof(char) is always 1 by
definition, and using it only serves to obfuscate the code.
if (s) memcpy (s, data, l);
return s;
}

struct list ** add_item (struct list ** tail, char * data) {
if (NULL == tail ||
NULL != *tail ||
(NULL == (*tail = (struct list *) malloc(sizeof(struct list))))
)
return NULL;
(*tail)->mybuff = copystr (data);
(*tail)->next = NULL;
return &((*tail)->next);
}
All this unnecessary complication to use a tail pointer. The code
is also obfuscated by more foolish and unnecessary casts.

Of course you have neglected to ever define struct list. The code
will not even compile.

void printlist (struct list * head) {
while (head) {
printf ("%s\n", head->mybuff);
head = head->next;
}
}
Having neglected to #include <stdio.h> there is no prototype for
printf in scope. The code thus invokes undefined behavior, and is
not suitable for this newsgroup.

void destroylist (struct list ** tail) {
if (NULL == tail) return;
while (*tail) {
struct list ** next = &((*tail)->next);
(*tail)->next = NULL;
free ((*tail)->mybuff);
free (*tail);
tail = next;
}
}
If tail is expected to point to the last item in a list, tail->next
would normally be NULL. Once more the code is obfuscated and
unnecessarily verbose.

int main () {
int i;
char buff[51];
struct list * top = NULL, * cur, ** tailptr;

for (tailptr = &top, i=0; i < 10; i++) {
getline (buff, 50);
tailptr = add_item (tailptr, buff);
}

printlist (top);
destroylist (&top);
return 0;
}


All in all, I do not recommend studying the above.

Should the OP really want a list formed in the so called normal
order (rather than reversed, as I demonstrated earlier) he can do
so with perfectly portable code. However he would be well advised
to make a few block diagrams showing the actual form of the list
when empty, when holding one item, and when holding more than one
item, and how to make the transitions between them. He might well
consider defining a listheader type which can hold both head and
tail pointers, something like:

struct listheader {
struct node *head, *tail;
}

and he can then declare instances as:

struct listheader mylist = {NULL, NULL};

for the initial condition. Now the functions can operate on:

errinfo_t somefunction(struct listheader *thelist, /* data */);

and avoid all that fuzzy thinking.

--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 14 '05 #9

P: n/a
CBFalconer wrote:
we******@gmail.com wrote:
Xarky wrote:
I am writing a linked list in the following way.
... snip ...

"//" is not a portable way of expressing a comment in C. Not that
portability is an issue in this newsgroup. (You can also use
fgets() as an alternative to the function you've written.)
On the contrary, it is extremely important. The point about // is
that it is only portably usable on C99 compliant systems,


Which basically dont exist. Therefore its not portable. Just how
shallow are your powers of reasoning?
2. Ok, there is also a common obfuscation that people do when they
make linked lists of distinguishing the "empty list" case from the
other cases. Actually both cases are the same if you think of the
"current tail pointer" as a reference to the link point, rather
than the upper container of the link point. I.e., tail should be
&("last"->next) rather than just "last". In this way the "current
tail pointer" for empty list is just the address of the top-of-list
pointer, and is just the address of the last "->next" record in the
list.


As I demonstrated in an earlier posting in this thread, there is no
need for the complication of maintaining a tail pointer.


Your implementation adds entries to the "top" of the list. The OP
clearly was trying to implement a semantic which adds entries to the
tail. There is a difference.
I've demonstrated this in the code below:

static char * copystr (const char * data) {
int l = strlen(data) + 1;
char * s = (char *) malloc (sizeof (char) * l);


The cast is superfluous, and only serves to hide the error of
failing to #include <stdlib.h>. sizeof(char) is always 1 by
definition, and using it only serves to obfuscate the code.


Illustrating my point about not caring about portability perfectly.
The cast has to be there for this to correctly compile with C++
compilers. A lot of C development done today is actually on C++
compilers.
if (s) memcpy (s, data, l);
return s;
}

struct list ** add_item (struct list ** tail, char * data) {
if (NULL == tail ||
NULL != *tail ||
(NULL == (*tail = (struct list *) malloc(sizeof(struct list)))) )
return NULL;
(*tail)->mybuff = copystr (data);
(*tail)->next = NULL;
return &((*tail)->next);
}


All this unnecessary complication to use a tail pointer. The code
is also obfuscated by more foolish and unnecessary casts.


There is one cast above and I've explained why this is necessary. I'm
sorry if this code is too complicated for you, but it solves the
problem in a way that is as close to what the OP was trying to do as
possible. If you have a specific question, I can explain whichever
part of this code you don't understand.
Of course you have neglected to ever define struct list. The code
will not even compile.
I don't think the OP will have any trouble with it considering he has
defined it. I never claimed it was a complete program. The OP still
has to put it together.
All in all, I do not recommend studying the above.
But you do recommend changing the algorithm and basically mixing up the
semantics of a queue versus a stack?

The OP can't learn anything from your code -- it does the wrong thing.
Besides being correct, my code demonstrates an important idea in
simplifying linked list management. I.e., that the empty, singleton
and other cases are not actually different in any way, and don't need
to be dealt with in seperate cases.
[...] However he would be well advised
to make a few block diagrams showing the actual form of the list
when empty, when holding one item, and when holding more than one
item, and how to make the transitions between them. He might well
consider defining a listheader type which can hold both head and
tail pointers, something like:

struct listheader {
struct node *head, *tail;
}
You of course miss the whole point of unsing an additional indirection.
"tail" should not be declared like that. It just causes you to have
extra cases that get pushed upwards into the usage semantics. I.e.,
the code doubles in size for no good reason.

Furthermore, anyone who has dealt with linked lists knows that having a
pointer to the end is not necessarily an intrinsic part of a linked
list -- for example if you wish to implement an "insert" function
instead of an "append" function, then the tail is not of any relevance.
I.e., including a definition of a tail into a "listheader" (which may
be redundant and unnecessary) is not always desirable.
Now the functions can operate on:

errinfo_t somefunction(struct listheader *thelist, /* data */);

and avoid all that fuzzy thinking.


Yeah, you seem to be big on avoiding thinking of any kind.

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #10

P: n/a


CBFalconer wrote:
2. Ok, there is also a common obfuscation that people do when they
make linked lists of distinguishing the "empty list" case from the
other cases. Actually both cases are the same if you think of the
"current tail pointer" as a reference to the link point, rather
than the upper container of the link point. I.e., tail should be
&("last"->next) rather than just "last". In this way the "current
tail pointer" for empty list is just the address of the top-of-list
pointer, and is just the address of the last "->next" record in the
list.

As I demonstrated in an earlier posting in this thread, there is no
need for the complication of maintaining a tail pointer.


It really is unclear on the requirements of the OP. Should the
op need to maintain a data model that requires sequentually
adding data to the end of the link then your demonstration
would not be the preferred method, as the demonstration adds
data to the root(head) of the list. IMO, there is not
much complication in maintaining a tail pointer. A model
as you have described below would be a fine approach.
.....................SNIP....................


Should the OP really want a list formed in the so called normal
order (rather than reversed, as I demonstrated earlier) he can do
so with perfectly portable code. However he would be well advised
to make a few block diagrams showing the actual form of the list
when empty, when holding one item, and when holding more than one
item, and how to make the transitions between them. He might well
consider defining a listheader type which can hold both head and
tail pointers, something like:

struct listheader {
struct node *head, *tail;
}

and he can then declare instances as:

struct listheader mylist = {NULL, NULL};

for the initial condition. Now the functions can operate on:

errinfo_t somefunction(struct listheader *thelist, /* data */);

and avoid all that fuzzy thinking.


Yes!
Here is a hastily written and possibly deficient example
that I hope is not fuzzy.

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

#define MAX_DATA 50

struct node
{
struct node *next;
char data[MAX_DATA];
};

typedef struct QUEUE
{
struct node *head;
struct node *tail;
} QUEUE;

/* Prototyes */
struct node *pushQUEUE(QUEUE *p, char *data);
char *popQUEUE(QUEUE *p);
void freeQUEUE(QUEUE *p);

int main(main)
{
char *data;
QUEUE fifo = {NULL};
int i;

pushQUEUE(&fifo,"1111111111");
pushQUEUE(&fifo,"2222222222");
pushQUEUE(&fifo,"3333333333");
for(i = 0 ; i < 2 &&(data = popQUEUE(&fifo)) ;i++ )
printf("POP \"%s\"\n",data);
pushQUEUE(&fifo,"4444444444");
putchar('\n');
for( ; (data = popQUEUE(&fifo));i++ )
printf("POP \"%s\"\n",data);
freeQUEUE(&fifo);
return 0;
}

struct node *pushQUEUE(QUEUE *p, char *data)
{
struct node *tmp;

if((tmp = calloc(1,sizeof *tmp)) != NULL)
{
strncat(tmp->data,data,MAX_DATA);
tmp->next = NULL;
if(!p->tail) p->head = tmp;
else p->tail->next = tmp;
p->tail = tmp;
}
return tmp;
}

char *popQUEUE(QUEUE *p)
{
static char s[MAX_DATA];
struct node *tmp;

if(!p->head) return NULL;
strcpy(s,p->head->data);
tmp = p->head->next;
free(p->head);
if((p->head = tmp) == NULL) p->tail = NULL;
return s;
}

void freeQUEUE(QUEUE *p)
{
struct node *tmp;

for( ; (p->head); p->head = tmp)
{
tmp = p->head->next;
free(p->head);
}
p->tail = NULL;
return;
}

--
Al Bowers
Tampa, Fl USA
mailto: xa******@myrapidsys.com (remove the x to send email)
http://www.geocities.com/abowers822/

Nov 14 '05 #11

P: n/a


we******@gmail.com wrote:
CBFalconer wrote:

struct list ** add_item (struct list ** tail, char * data) {
if (NULL == tail ||
NULL != *tail ||
(NULL == (*tail = (struct list *) malloc(sizeof(struct
list))))
)
return NULL;
(*tail)->mybuff = copystr (data);
(*tail)->next = NULL;
return &((*tail)->next);
}


All this unnecessary complication to use a tail pointer. The code
is also obfuscated by more foolish and unnecessary casts.

There is one cast above and I've explained why this is necessary. I'm
sorry if this code is too complicated for you, but it solves the
problem in a way that is as close to what the OP was trying to do as
possible. If you have a specific question, I can explain whichever
part of this code you don't understand.


It seems that the OP wants a pointer to the tail of the
list for some reason unknown. It seems strange that you
would define a function add_item that will would adultrate
that precious tail pointer should function malloc return NULL.
Perhaps you have a reason with would be explicable but on the
surface it looks to ba a clumsy way to define the function.

--
Al Bowers
Tampa, Fl USA
mailto: xa******@myrapidsys.com (remove the x to send email)
http://www.geocities.com/abowers822/

Nov 14 '05 #12

P: n/a
we******@gmail.com writes:
CBFalconer wrote:
we******@gmail.com wrote:
> Xarky wrote:
>> I am writing a linked list in the following way.
>>

... snip ...
>
> "//" is not a portable way of expressing a comment in C. Not that
> portability is an issue in this newsgroup. (You can also use
> fgets() as an alternative to the function you've written.)


On the contrary, it is extremely important. The point about // is
that it is only portably usable on C99 compliant systems,


Which basically dont exist. Therefore its not portable.

[personal insult snipped]

Nobody was disputing your statement that "//" comments aren't
portable. CBFalconer's "On the contrary" was in response to your
claim that portability is not an issue in this newsgroup. In fact,
portability is an always has been an extremely important issue in this
newsgroup.

[...]
> I've demonstrated this in the code below:
>
> static char * copystr (const char * data) {
> int l = strlen(data) + 1;
> char * s = (char *) malloc (sizeof (char) * l);


The cast is superfluous, and only serves to hide the error of
failing to #include <stdlib.h>. sizeof(char) is always 1 by
definition, and using it only serves to obfuscate the code.


Illustrating my point about not caring about portability perfectly.
The cast has to be there for this to correctly compile with C++
compilers. A lot of C development done today is actually on C++
compilers.


There is rarely any need for C code to compile correctly with C++
compilers. That's what C compilers are for. Code compiled with C++
compilers is called C++; there are several newsgroups dedicated to it.

As you know, C and C++ are closely related but distinct languages.

Casting the result of malloc() is unnecessary in C, and can (depending
on the compiler) mask the error of failing to #include <stdlib.h>.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #13

P: n/a
we******@gmail.com wrote:
CBFalconer wrote:
we******@gmail.com wrote:

.... snip ...
I've demonstrated this in the code below:

static char * copystr (const char * data) {
int l = strlen(data) + 1;
char * s = (char *) malloc (sizeof (char) * l);


The cast is superfluous, and only serves to hide the error of
failing to #include <stdlib.h>. sizeof(char) is always 1 by
definition, and using it only serves to obfuscate the code.


Illustrating my point about not caring about portability
perfectly. The cast has to be there for this to correctly
compile with C++ compilers. A lot of C development done today
is actually on C++ compilers.


Marvellous. You write poor obfuscated error-prone C code in order
to avoid using a C compiler, and claim the result is portable C. I
am blinded by your keen intellect. I assume your code compiles on
a Fortran compiler? Pascal? Ada? MIX assembler? Should I let
the cat walk over the keyboard to select the compiler?

--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!

Nov 14 '05 #14

P: n/a
Al Bowers wrote:
[...] It seems that the OP wants a pointer to the tail of the
list for some reason unknown.
Unknown?!?! Look closely. It causes the algorithm to stop attempting
to add nodes. It is essentially a simple feedback that lets you know
that you are out of memory (or more generally that an add_item has
failed).
[...] It seems strange that you
would define a function add_item that will would adultrate
that precious tail pointer should function malloc return NULL.
Strange how?
Perhaps you have a reason with would be explicable but on the
surface it looks to ba a clumsy way to define the function.


I don't understand your confusion. The tail is overwritten with NULL
when malloc has failed on you, so you can test it easily afterwards ...
what would you suggest as an alternative behavior?

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #15

P: n/a
Keith Thompson wrote:
Nobody was disputing your statement that "//" comments aren't
portable.
Nobody else brought it up either. This doesn't tell you something?
[...] CBFalconer's "On the contrary" was in response to your
claim that portability is not an issue in this newsgroup. In fact,
portability is an always has been an extremely important issue in
this newsgroup.
I simply don't think that's the case.
[...]
> I've demonstrated this in the code below:
>
> static char * copystr (const char * data) {
> int l = strlen(data) + 1;
> char * s = (char *) malloc (sizeof (char) * l);

The cast is superfluous, and only serves to hide the error of
failing to #include <stdlib.h>. sizeof(char) is always 1 by
definition, and using it only serves to obfuscate the code.
Illustrating my point about not caring about portability perfectly.
The cast has to be there for this to correctly compile with C++
compilers. A lot of C development done today is actually on C++
compilers.


There is rarely any need for C code to compile correctly with C++
compilers.


That's not true. Many people who write C++ code, also really write a
lot of C code. And therefore also consume C code written by others.
Its not rare at all. C++ people don't use entirely different concepts
to implement linked lists.
[...] That's what C compilers are for. Code compiled with C++
compilers is called C++; there are several newsgroups dedicated to
it.
Just because you decide to live in a small little cubby hole doesn't
mean that other people have chosen to do the same. The WATCOM C++
compiler, often produces substantially faster code than their C
compiler, BTW.
As you know, C and C++ are closely related but distinct languages.

Casting the result of malloc() is unnecessary in C, and can
(depending on the compiler) mask the error of failing to #include
<stdlib.h>.


Not casting malloc() masks the error of not being able to compile with
a C++ compiler. Of the two points of view, which do you think is more
aligned to the issue of portability?

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #16

P: n/a
we******@gmail.com writes:
Keith Thompson wrote:
Nobody was disputing your statement that "//" comments aren't
portable.
Nobody else brought it up either. This doesn't tell you something?


No, it doesn't. I've seen the non-portability of "//" comments
mentioned here numerous times.
[...] CBFalconer's "On the contrary" was in response to your
claim that portability is not an issue in this newsgroup. In fact,
portability is an always has been an extremely important issue in
this newsgroup.


I simply don't think that's the case.


Then I humbly submit that you should consider paying closer attention.

[...]
There is rarely any need for C code to compile correctly with C++
compilers.


That's not true.

[snip]

Yes, it is. This has been discussed at length here many times.

[snip]
Not casting malloc() masks the error of not being able to compile with
a C++ compiler. Of the two points of view, which do you think is more
aligned to the issue of portability?


Not being able to compile with a C++ compiler is not an error.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #17

P: n/a
we******@gmail.com wrote:
Al Bowers wrote:
[...] It seems that the OP wants a pointer to the tail of the
list for some reason unknown.


Unknown?!?! Look closely. It causes the algorithm to stop attempting
to add nodes. It is essentially a simple feedback that lets you know
that you are out of memory (or more generally that an add_item has
failed).


Which is a wrong way to handle such situations. If add_item() fails, it
should return a failure status, not fiddle with a pointer.

Besides, the OP's code never even checks what happens to that pointer.
If malloc() returns a null pointer, it merrily writes through it as if
nothing untoward has happened.
Perhaps you have a reason with would be explicable but on the
surface it looks to ba a clumsy way to define the function.


I don't understand your confusion. The tail is overwritten with NULL
when malloc has failed on you, so you can test it easily afterwards ...
what would you suggest as an alternative behavior?


Make add_item() an int function rather than void, return a success
value, and for heavens' sake, _check_ that malloc() succeeded!
Alternatively, make add_item() return a struct list *, to the added
member if succesful, null if not. And ditto on malloc().

Richard
Nov 14 '05 #18

P: n/a
we******@gmail.com wrote:
Keith Thompson wrote:
Nobody was disputing your statement that "//" comments aren't
portable.


Nobody else brought it up either. This doesn't tell you something?
[...] CBFalconer's "On the contrary" was in response to your
claim that portability is not an issue in this newsgroup. In fact,
portability is an always has been an extremely important issue in
this newsgroup.


I simply don't think that's the case.


That's because you don't know what "portable" means.
There is rarely any need for C code to compile correctly with C++
compilers.


That's not true. Many people who write C++ code, also really write a
lot of C code. And therefore also consume C code written by others.


And if so, they'd better learn the difference. Broken C++ users are no
excuse for us to subject our C code to uglification as well.
Casting the result of malloc() is unnecessary in C, and can
(depending on the compiler) mask the error of failing to #include
<stdlib.h>.


Not casting malloc() masks the error of not being able to compile with
a C++ compiler.


That is not an error, it is a highly useful feature.

I'd tell you to get a clue, but I don't think you're able to.

Richard
Nov 14 '05 #19

P: n/a
In article <11**********************@o13g2000cwo.googlegroups .com>
<we******@gmail.com> wrote:
Not casting malloc() masks the error of not being able to compile with
a C++ compiler.


I see. Do you avoid classes, "new", exceptions, and all other C++
features so that your C++ code can be compiled with a C compiler?
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #20

P: n/a
Chris Torek wrote:
we******@gmail.com wrote:
Not casting malloc() masks the error of not being able to compile
with a C++ compiler.


I see. Do you avoid classes, "new", exceptions, and all other C++
features so that your C++ code can be compiled with a C compiler?


Who says I write C++ code? I *do* write code that other people
consume. I *do* know that C++ compilers are still being maintained
while C compilers are basically being relegated to legacy support --
meaning that the differences I see in performance today will only
increase over time.

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #21

P: n/a
Richard Bos wrote:
we******@gmail.com wrote:
Al Bowers wrote:
[...] It seems that the OP wants a pointer to the tail of the
list for some reason unknown.
Unknown?!?! Look closely. It causes the algorithm to stop
attempting to add nodes. It is essentially a simple feedback
that lets you know that you are out of memory (or more generally
that an add_item has failed).


Which is a wrong way to handle such situations. If add_item()
fails, it should return a failure status, not fiddle with a
pointer.


Explain how this is wrong.
Besides, the OP's code never even checks what happens to that
pointer. If malloc() returns a null pointer, it merrily writes
through it as if nothing untoward has happened.
You might have noticed I rewrote a lot of the OP's code.
Perhaps you have a reason with would be explicable but
on the surface it looks to ba a clumsy way to define the
function.


I don't understand your confusion. The tail is overwritten
with NULL when malloc has failed on you, so you can test it
easily afterwards ... what would you suggest as an
alternative behavior?


Make add_item() an int function rather than void, return a
success value, and for heavens' sake, _check_ that malloc()
succeeded!


Ok, these semantics are so far almost isomorphic to what I have
provided. Of course I don't have additional concepts such as a "error
status" in there. You just compare the tail iterator against NULL
instead of checking for a particular error code, but its the same
thing.

But there's a slight advantage to my method -- you can *ignore* the
error in your inner loops, and no harm will come of it, while retaining
this error condition that you can check after your inner loop is done.
This helps solve the common problem of control-code spaghetti when
dealing with errors (since C doesn't have exception handling.)
Alternatively, make add_item() return a struct list *, to
the added member if succesful, null if not.
Ok, go back and review my design. The iteration happens by taking an
iterator as input, and giving the iterator back as output. In this
case I chose the address of the last link pointer because its trivial
to initialize, allows you to avoid the "empty case" differentiation,
and is a single parameter the can be efficiently returned or passed as
a single parameter.

If I point to something artificial like the last entry after its been
added (the fact that this is artificial is the point I was trying to
make), then this complicates either initialization, or requires more
input parameters, or more output parameters, etc.

My way is less code, fewer sematics, and really exposes the core
functionality required to support linked lists with an append
operation. This should be more conducive to learning how linked lists
work, which I surmised that the OP would be helped by.
And ditto on malloc().


Huh? There is no "unchecked dereferencing" in the code I supplied
(except for the strlen() call, but by being "static" I assumed that the
programmer would limit the exposure, and "coddle" that function
correctly.)

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #22

P: n/a
On 9 May 2005 02:20:08 -0700, we******@gmail.com
<we******@gmail.com> wrote:
Keith Thompson wrote:
Nobody was disputing your statement that "//" comments aren't
portable.
Nobody else brought it up either. This doesn't tell you something?


It could tell you that most people aren't interested in arguing with you
and have you in their killfiles. Or that it's Tuesday and people are
worrying about the weather, or lots of other things...
[...] CBFalconer's "On the contrary" was in response to your
claim that portability is not an issue in this newsgroup. In fact,
portability is an always has been an extremely important issue in
this newsgroup.


I simply don't think that's the case.


Have you read comp.lang.c? It often seems that portability is the only
thing which matters in clc...
There is rarely any need for C code to compile correctly with C++
compilers.


That's not true. Many people who write C++ code, also really write a
lot of C code.


Interesting assertion, I seem to be about the only one I know
personally. In my experience most people who write C++ don't write C
unless they have to do so.
And therefore also consume C code written by others.
When they do, it is usually compiled as C and then linked (often as a
library), just as they use code written in Fortran, Pascal, assembler,
etc.
Its not rare at all. C++ people don't use entirely different concepts
to implement linked lists.


Yes they do, C++ programmers use the STL list template (or the Boost
ones). Real C++ programmers, that is, rather than people who just use
it as a dialect of C. Try making that comment on comp.lang.c++ and
watch the flames.
[...] That's what C compilers are for. Code compiled with C++
compilers is called C++; there are several newsgroups dedicated to
it.


Just because you decide to live in a small little cubby hole doesn't
mean that other people have chosen to do the same. The WATCOM C++
compiler, often produces substantially faster code than their C
compiler, BTW.


There are Fortran compilers which probably produse faster code than
both. So?
As you know, C and C++ are closely related but distinct languages.

Casting the result of malloc() is unnecessary in C, and can
(depending on the compiler) mask the error of failing to #include
<stdlib.h>.


Not casting malloc() masks the error of not being able to compile with
a C++ compiler. Of the two points of view, which do you think is more
aligned to the issue of portability?


Casting malloc() masks the much bigger error of using malloc() in C++ at
all (C++ programmers would call it evil). But then you aren't a C++
programmer, are you?

Portability is not about being able to compile code written for one
language with a compiler designed for a different language, it's about
being able to compile and use the /same/ language on different
implementations and systems.

Chris C
Nov 14 '05 #23

P: n/a
we******@gmail.com wrote:
Chris Torek wrote:
we******@gmail.com wrote:
Not casting malloc() masks the error of not being able to compile
with a C++ compiler.


I see. Do you avoid classes, "new", exceptions, and all other C++
features so that your C++ code can be compiled with a C compiler?


Who says I write C++ code? I *do* write code that other people
consume.


So? There are hamburger chains that produce food out of bad meat,
and whose consumers become sick. Some die. No extra charge for
ptomaine.
I *do* know that C++ compilers are still being maintained
while C compilers are basically being relegated to legacy support --
meaning that the differences I see in performance today will only
increase over time.


FYI compilers generally do not require revision unless the language
changes. The modern practice is to compile to an intermediate
form, which is then fed to the optimizers and code generators.
This area is undergoing continuous revision to improve optimization
and adapt to new hardware. It is also language independant by
design. It works best when the compiler front end has been able to
diagnose and reject any errors. Foolish techniques such as useless
casts (in C) prevent this.

--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 14 '05 #24

P: n/a
On 8 May 2005 20:25:11 -0700, in comp.lang.c , we******@gmail.com
wrote:
CBFalconer wrote:
we******@gmail.com wrote:
> Xarky wrote:
>> I am writing a linked list in the following way.
>>

... snip ...
>
> "//" is not a portable way of expressing a comment in C. Not that
> portability is an issue in this newsgroup. (You can also use
> fgets() as an alternative to the function you've written.)


On the contrary, it is extremely important. The point about // is
that it is only portably usable on C99 compliant systems,


Which basically dont exist. Therefore its not portable. Just how
shallow are your powers of reasoning?


Apparently deeper than yours. I know of almost no compiler that won't
accept // comments, and several even do so when in fully compliant
mode.
Are you stuck in a timewarp?
--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
CLC readme: <http://www.ungerhu.com/jxh/clc.welcome.txt>

----== Posted via Newsfeeds.Com - Unlimited-Uncensored-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! 120,000+ Newsgroups
----= East and West-Coast Server Farms - Total Privacy via Encryption =----
Nov 14 '05 #25

P: n/a
Mark McIntyre <ma**********@spamcop.net> writes:
Apparently deeper than yours. I know of almost no compiler that won't
accept // comments, and several even do so when in fully compliant
mode.


That's a contradiction in terms. Support for // comments means
that a compiler is not fully compliant (to C89).
--
Ben Pfaff
email: bl*@cs.stanford.edu
web: http://benpfaff.org
Nov 14 '05 #26

P: n/a
websn...@gmail.com wrote:
Keith Thompson wrote:

Casting the result of malloc() is unnecessary in C, and can
(depending on the compiler) mask the error of failing to
#include <stdlib.h>.


Not casting malloc() masks the error of not being able to compile
with a C++ compiler. Of the two points of view, which do you
think is more aligned to the issue of portability?


Your entire program has the error of not being able to compile
with a Java compiler. You'd better delete the code (I sure have).

Nov 14 '05 #27

P: n/a
Ben Pfaff <bl*@cs.stanford.edu> writes:
Mark McIntyre <ma**********@spamcop.net> writes:
Apparently deeper than yours. I know of almost no compiler that won't
accept // comments, and several even do so when in fully compliant
mode.


That's a contradiction in terms. Support for // comments means
that a compiler is not fully compliant (to C89).


Not necessarily. A conforming C89 compiler is free to accept //
comments, as long as it issues a diagnostic for them. (Though I think
there may be some obscure cases where recognizing // as a comment
delimiter makes the difference between two different interpretations
of some code.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #28

P: n/a
Mark McIntyre wrote:
we******@gmail.com wrote:
CBFalconer wrote:
we******@gmail.com wrote:
> Xarky wrote:
>> I am writing a linked list in the following way.
>>
... snip ...
>
> "//" is not a portable way of expressing a comment in C. Not that > portability is an issue in this newsgroup. (You can also use
> fgets() as an alternative to the function you've written.)

On the contrary, it is extremely important. The point about // is
that it is only portably usable on C99 compliant systems,


Which basically dont exist. Therefore its not portable. Just how
shallow are your powers of reasoning?


Apparently deeper than yours. I know of almost no compiler that won't
accept // comments, and several even do so when in fully compliant
mode.
Are you stuck in a timewarp?


No. But perhaps gcc is. You need to pass it a "-std=c99" (which puts
it into this meaningless mode, where it compiles to no existing
standard) to trick it into accepting the //s.

Remind me ... you are the one who suggested me that empty strings are
illegal right?

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #29

P: n/a
Chris Croughton wrote:
<we******@gmail.com> wrote:
Keith Thompson wrote:
Nobody was disputing your statement that "//" comments aren't
portable.
Nobody else brought it up either. This doesn't tell you something?


It could tell you that most people aren't interested in arguing with

you and have you in their killfiles.
Oh yeah I certainly noticed this ... you can tell by who everyone is
replying to in this thread.
[...] CBFalconer's "On the contrary" was in response to your
claim that portability is not an issue in this newsgroup. In fact, portability is an always has been an extremely important issue in
this newsgroup.


I simply don't think that's the case.


Have you read comp.lang.c?


Only on and off for about 15 years.
[...] It often seems that portability is the only thing which
matters in clc...
You are mistaking standards lawyering for concern for portability.
There's a question of supporting what's there, and there's what you
*believe* is supposed to be there.
There is rarely any need for C code to compile correctly with C++
compilers.


That's not true. Many people who write C++ code, also really write a
lot of C code.


Interesting assertion, I seem to be about the only one I know
personally.


Well I don't know who you know personally, but interesting that you
didn't have to look far to find one.
[...] In my experience most people who write C++ don't write C
unless they have to do so.
Really? I've seemed to see a lot of beginners in C++ who don't
understand that distinction who end up writing a lot of C code,
unknowingly, because they happen to not need to the C++ machinery for
some of their code.
And therefore also consume C code written by others.


When they do, it is usually compiled as C and then linked (often as a
library), just as they use code written in Fortran, Pascal,

assembler, etc.
That only works in C if you are willing to write 'extern "C"' around
all your C .h files. A much simpler way is to recompile the C code
with your C++ compiler.
Its not rare at all. C++ people don't use entirely different concepts to implement linked lists.


Yes they do, C++ programmers use the STL list template (or the Boost
ones).


For a linked list? Maybe a vector, which is a different thing. But
not for a linked list.
[...] Real C++ programmers, that is, rather than people who just use
it as a dialect of C. Try making that comment on comp.lang.c++ and
watch the flames.
Well, they will tell me to use a "stack" or a "deque" depending on
which I want to use.
[...] That's what C compilers are for. Code compiled with C++
compilers is called C++; there are several newsgroups dedicated to
it.


Just because you decide to live in a small little cubby hole doesn't
mean that other people have chosen to do the same. The WATCOM C++
compiler, often produces substantially faster code than their C
compiler, BTW.


There are Fortran compilers which probably produse faster code than
both. So?


It costs a lot to port your code between Fortran and C. Making your
code C and C++ compatible is fairly straightforward, and you get the
speed increases from upgrading compilers at much more reasonable cost.

C as a language is clearly in decline, while C++ is not (though a lot
of its mindshare got robbed by Java.) And vendors who make C compilers
generally make C and C++ combined products. That means in the long
run, the vendor will treat their C compiler as a "checkmark" while
treating their C++ compiler as the focus of all serious future
development.

Its just practical considerations. If you write code with works in C
and C++, then your code gets faster without changes just by waiting for
compilers to get better. If you make it C-only, then it stays at the
current performance level, and can only improve by paying for hardware
upgrades.
As you know, C and C++ are closely related but distinct languages.

Casting the result of malloc() is unnecessary in C, and can
(depending on the compiler) mask the error of failing to #include
<stdlib.h>.


Not casting malloc() masks the error of not being able to compile with a C++ compiler. Of the two points of view, which do you think is more aligned to the issue of portability?


Casting malloc() masks the much bigger error of using malloc() in C++

at all (C++ programmers would call it evil). But then you aren't a C++
programmer, are you?
Excuse me? C++ does not expose an equivalent to "realloc()" in their
new/delete concepts. So using malloc in C++ is not wrong, its just not
generally used for base-object memory management.
Portability is not about being able to compile code written for one
language with a compiler designed for a different language, it's about being able to compile and use the /same/ language on different
implementations and systems.


You must be from the Scott McClelland/Ari Fliescher school of
definitionism. Portability refers to using one code based on many
different platforms (and compilers) when talking about code, and making
sure the semantics are equivalent on different platforms when talking
about whole applications.

Making sure a language works on multiple platforms? In what universe
is that not just "standardization"?

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #30

P: n/a
In article <11**********************@z14g2000cwz.googlegroups .com>,
<we******@gmail.com> wrote:
C as a language is clearly in decline, while C++ is not (though a lot
of its mindshare got robbed by Java.) And vendors who make C compilers
generally make C and C++ combined products. That means in the long
run, the vendor will treat their C compiler as a "checkmark" while
treating their C++ compiler as the focus of all serious future
development.


My understanding (perhaps incorrect) is that most serious
development for procedural compilers these days is based upon
having a parser layer that produces intermediate code, with all
the optimization done at the intermediate code level. The
bulk of the work is in the optimization phases, so even if the
parsers get tuned differently, the optimization phases and generated
code grow at the same rate for all the compilers implimented
on the common platform.

There could certainly be differences in the prioritization of
library implementations, or of prioritization of feature change
implementation, but the effect is somewhat different than implied
by your paragraph.
I am not convinced that "C as a language is clearly in decline".
It might perhaps have a lower percentage of the market than it once
did, but the market is expanding.

I think whether I implemented in C or C++ would depend a fair bit
upon political factors (for stuff I'm paid to do); but when given
the choice of languages, my determination would depend on
tradeoffs between efficiency and program structure. There are an
amazing number of applications that don't *need* classes.

C++ as a development methodology is based upon the premise that
you will put together a library of objects and that the world will
rejoice and yeah verity the virtual classes will be fruitful
all the days of thy life. The reality is somewhat different:
yup, there are definitely some things that get implimented
over and over again that can be shared, but there is so much
application-specific logic that (according to some material I
was reading) -most- classes get used for at most 2 or 3 projects
and then get left behind. That's a lot of time spent finding
beautiful abstractions, that doesn't end up going very far...

So bring on the C for projects where one actually knows what one
is doing ahead of time, where one wants to write non-bloated code
to get in, do something, and get out again.
--
I was very young in those days, but I was also rather dim.
-- Christopher Priest
Nov 14 '05 #31

P: n/a


we******@gmail.com wrote:
Al Bowers wrote:
[...] It seems strange that you
would define a function add_item that will would adultrate
that precious tail pointer should function malloc return NULL.

Strange how?


I sorry, I meant adulterate.
By this I meant 'to tamper with'. Your "tail" and "tailptr"
are type struct list **, which you use as a pointer to the
pointer to the last node in the list. Now, should the malloc
fail some nodes into the the list construct,
the way you have written the code, variable tail and tailptr
become value NULL. This means you no longer have a pointer
to the end of the list. This is not a problem with the code
you have written because you simply prohibit any further
attempts to continue building the list and that you do
not need a valid tailptr for anything else. If one wanted to
attempt to free other memory areas and retry building, one
whould have to first traverse the list again to get a pointer
to the last node. My preference would be to have the tailptr
always point to the last node in the list or have value NULL
should there be no list.

However the above is not the reason the posted example is
broken. That involves another function.

--
Al Bowers
Tampa, Fl USA
mailto: xa******@myrapidsys.com (remove the x to send email)
http://www.geocities.com/abowers822/

Nov 14 '05 #32

P: n/a
Walter Roberson wrote:
we******@gmail.com wrote:
C as a language is clearly in decline, while C++ is not (though a
lot of its mindshare got robbed by Java.) And vendors who make C
compilers generally make C and C++ combined products. That means
in the long run, the vendor will treat their C compiler as a
"checkmark" while treating their C++ compiler as the focus of all
serious future development.
My understanding (perhaps incorrect) is that most serious
development for procedural compilers these days is based upon
having a parser layer that produces intermediate code, with all
the optimization done at the intermediate code level. The
bulk of the work is in the optimization phases, so even if the
parsers get tuned differently, the optimization phases and
generated code grow at the same rate for all the compilers
implimented on the common platform.


Yes, but it still takes quite a bit of work to translate from the
langauge level to this intermediate level. And it takes a lot of
testing to make sure its all working correctly. And different
languages will have different emphasis on optimization.

If a language vendor is not getting feedback about C, but is about C++,
and really cannot see C as anything other than "legacy support", then
regardless of how "modularly" they could do it, what would justify them
spending effort, going forward, on the older C compiler outside of
optimizing a few benchmarks?
There could certainly be differences in the prioritization of
library implementations, or of prioritization of feature change
implementation, but the effect is somewhat different than
implied by your paragraph.
This is what I mean. Any serious compiler vendor cannot view C as
anything other than an opportunity on a few fixed benchmarks, as well
as possible bug fix requests. This is why basically no vendor is
rushing to support the "C99" standard. Of the serious vendors, only
the gcc people (who are not beholden to a market driven considerations)
even made a serious go at full support. If you read the recent
slashdot article on gcc 4.0 you will see that C performance has not
changed at all -- in fact, the majority of the effort has gone towards
better C++ support.
I am not convinced that "C as a language is clearly in decline".
Ok ... about 10 years ago, there would be no question that C was the
dominant language. The best measures I have seen regarding mindshare
of C today is at about 30% (matched equally by C++ and Java.) The
direction of these trends is obvious. Look at schools today. They
don't teach C as a fundamental language -- they teach either C++ or
Java (or C# if they've been bribed by certain entities in the Pacific
Northwest.)
It might perhaps have a lower percentage of the market than it once
did, but the market is expanding.
That's like saying Apple's market is growing, even though their
percentage is going down. (By itself,) Its not a convincing argument
for software vendors to switch to just supporting the Mac.

Most new development is not done in C, unless for some reason, that's
the only thing the programmers know. At my previous place of
employment, we started with a C project, but as we started hiring more
developers, we concluded that there was enough C++ expertise amongst
them that we simply shifted the project into C++. I mean -- knowing C
is basically a subset of knowing C++, and we started in C -- and even
*that* was not enough of an argument to stay in C.
I think whether I implemented in C or C++ would depend a fair bit
upon political factors (for stuff I'm paid to do); but when given
the choice of languages, my determination would depend on
tradeoffs between efficiency and program structure. There are an
amazing number of applications that don't *need* classes.
Well, in fact, no applications need classes. That's not the point.
Most modern developers today simply *believe* programming with classes
is better. This causes a shift in mindshare, which causes a shift in
compiler development efforts etc.
C++ as a development methodology is based upon the premise that
you will put together a library of objects and that the world will
rejoice and yeah verity the virtual classes will be fruitful
all the days of thy life. The reality is somewhat different:
yup, there are definitely some things that get implimented
over and over again that can be shared, but there is so much
application-specific logic that (according to some material I
was reading) -most- classes get used for at most 2 or 3 projects
and then get left behind. That's a lot of time spent finding
beautiful abstractions, that doesn't end up going very far...


Trust me, I'm the last person on earth who would defend C++ as a
language. That isn't the point I was trying to make.

We are in a unique situation where C++ is starting to dominate over C,
certainly compiler development is shifting in that direction (and has
been for some years now), but we have the opportunity to take advantage
of the interesting fact that C++ is an approximation of a superset of
the C language. If you make your code compilable in C or C++, then you
get to leverage the ongoing improvements of C++ compilers while not
shifting your actual language and still remaining portable to C
compilers.

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #33

P: n/a
we******@gmail.com wrote:
Walter Roberson wrote:
we******@gmail.com wrote:
C as a language is clearly in decline, while C++ is not (though a
lot of its mindshare got robbed by Java.) And vendors who make C
compilers generally make C and C++ combined products. That means
in the long run, the vendor will treat their C compiler as a
"checkmark" while treating their C++ compiler as the focus of all
serious future development.
My understanding (perhaps incorrect) is that most serious
development for procedural compilers these days is based upon
having a parser layer that produces intermediate code, with all
the optimization done at the intermediate code level. The
bulk of the work is in the optimization phases, so even if the
parsers get tuned differently, the optimization phases and
generated code grow at the same rate for all the compilers
implimented on the common platform.


Yes, but it still takes quite a bit of work to translate from the
langauge level to this intermediate level. And it takes a lot of
testing to make sure its all working correctly. And different
languages will have different emphasis on optimization.


That work has *already* been done by the compiler vendors who know what
they are doing and therefore produce products worth using. They don't
need to redo it unless they change the definition of the intermediate
language, something that happens only rarely. On the rare occasions they
do change the intermediate language, they will change all the front ends
(including C) to use it so they only have one optimiser and code
generator to maintain. So that is completely irrelevant. So all
languages get the benefit of improvements to the optimiser.
If a language vendor is not getting feedback about C, but is about C++,
and really cannot see C as anything other than "legacy support", then
regardless of how "modularly" they could do it, what would justify them
spending effort, going forward, on the older C compiler outside of
optimizing a few benchmarks?
It has already been done on every compiler I've check since at least
'95. So there is no additional effort.
There could certainly be differences in the prioritization of
library implementations, or of prioritization of feature change
implementation, but the effect is somewhat different than
implied by your paragraph.


This is what I mean. Any serious compiler vendor cannot view C as
anything other than an opportunity on a few fixed benchmarks, as well
as possible bug fix requests. This is why basically no vendor is
rushing to support the "C99" standard. Of the serious vendors, only
the gcc people (who are not beholden to a market driven considerations)
even made a serious go at full support. If you read the recent
slashdot article on gcc 4.0 you will see that C performance has not
changed at all -- in fact, the majority of the effort has gone towards
better C++ support.


C still gets any changes to the optimiser since it uses the *same*
optimiser. It would be far more work to maintain a separate optimiser
for C. Support of C99 is a separate issue to the optimiser.

GNU have improved the speed of *compiling* C++. However, the C front end
does not have the same level of complexity and is more mature than the
C++ from end, so it does not have the same performance issues as the C++
front end. The same will apply to the libraries.
I am not convinced that "C as a language is clearly in decline".


Ok ... about 10 years ago, there would be no question that C was the
dominant language. The best measures I have seen regarding mindshare
of C today is at about 30% (matched equally by C++ and Java.) The
direction of these trends is obvious. Look at schools today. They
don't teach C as a fundamental language -- they teach either C++ or
Java (or C# if they've been bribed by certain entities in the Pacific
Northwest.)
It might perhaps have a lower percentage of the market than it once
did, but the market is expanding.


That's like saying Apple's market is growing, even though their
percentage is going down. (By itself,) Its not a convincing argument
for software vendors to switch to just supporting the Mac.


No one is suggesting that compiler vendors are, or should be, switching
to just supporting C. They support all languages they consider to be of
interest *including* C.
Most new development is not done in C, unless for some reason, that's
the only thing the programmers know. At my previous place of
employment, we started with a C project, but as we started hiring more
developers, we concluded that there was enough C++ expertise amongst
them that we simply shifted the project into C++. I mean -- knowing C
is basically a subset of knowing C++, and we started in C -- and even
*that* was not enough of an argument to stay in C.


Embedded work I used to be involved in was done in C rather than C++ for
a number of reasons which had absolutely nothing to do with the
languages known by the developers. I know this because as the first
developer in that section to start programming in C there I had to
actually learn C to do the job. We did not want the additional overheads
of an OO language or large libraries, we wanted something simple,
efficient, and supported on the widest range of HW possible including
small processors and DSPs.
I think whether I implemented in C or C++ would depend a fair bit
upon political factors (for stuff I'm paid to do); but when given
the choice of languages, my determination would depend on
tradeoffs between efficiency and program structure. There are an
amazing number of applications that don't *need* classes.


Well, in fact, no applications need classes. That's not the point.
Most modern developers today simply *believe* programming with classes
is better. This causes a shift in mindshare, which causes a shift in
compiler development efforts etc.


The work to try to efficiently implement classes etc. does not break
efficient implementation of C. It just tries to bring C++ to the level C
has already reached.
C++ as a development methodology is based upon the premise that
you will put together a library of objects and that the world will
rejoice and yeah verity the virtual classes will be fruitful
all the days of thy life. The reality is somewhat different:
yup, there are definitely some things that get implimented
over and over again that can be shared, but there is so much
application-specific logic that (according to some material I
was reading) -most- classes get used for at most 2 or 3 projects
and then get left behind. That's a lot of time spent finding
beautiful abstractions, that doesn't end up going very far...


Trust me, I'm the last person on earth who would defend C++ as a
language. That isn't the point I was trying to make.

We are in a unique situation where C++ is starting to dominate over C,
certainly compiler development is shifting in that direction (and has
been for some years now), but we have the opportunity to take advantage
of the interesting fact that C++ is an approximation of a superset of
the C language. If you make your code compilable in C or C++, then you
get to leverage the ongoing improvements of C++ compilers while not
shifting your actual language and still remaining portable to C
compilers.


The C front ends are in maintenance because they are mature products
that don't need major work unless you want to implement C99. The C++
front ends are still working towards that level of maturity. As to the
optimisers, as has been stated already they are common and so
improvements benefit both languages.
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.
Nov 14 '05 #34

P: n/a
On Mon, 09 May 2005 23:44:06 +0000, Keith Thompson wrote:
Ben Pfaff <bl*@cs.stanford.edu> writes:
Mark McIntyre <ma**********@spamcop.net> writes:
Apparently deeper than yours. I know of almost no compiler that won't
accept // comments, and several even do so when in fully compliant
mode.


That's a contradiction in terms. Support for // comments means
that a compiler is not fully compliant (to C89).


Not necessarily. A conforming C89 compiler is free to accept //
comments, as long as it issues a diagnostic for them. (Though I think
there may be some obscure cases where recognizing // as a comment
delimiter makes the difference between two different interpretations
of some code.)


Exactly, a conforming C90 compiler cannot accept // comments as doing so
would cause it to misinterpret some strictly conforming programs. The
cases aren't likely to occur in practice but that doesn't stop it being a
conformance issue. E.g.

#include <stdio.h>

int main(void)
{
printf ("%d\n", 10 //*
/**/ -2);
return 0;
}

should output -5 in C90 and 8 in C99.

Lawrence



Nov 14 '05 #35

P: n/a

Mark McIntyre wrote:
Apparently deeper than yours. I know of almost no compiler that won't
accept // comments, and several even do so when in fully compliant
mode.

We ran the problem on the last project I worked on, the compiler (as
configured) didn't accept them. It's the cross-compiler for Tornado
AE653, with a PowerPC target. The makefile just calls the it ccppc, but
I'm sure it's some sort of gnu product, as pretty much all Tornado
stuff is.

Caused problems for some of the guys used to using // comments even in
C.


Brian

Nov 14 '05 #36

P: n/a
Lawrence Kirby <lk****@netactive.co.uk> writes:
On Mon, 09 May 2005 23:44:06 +0000, Keith Thompson wrote:
Ben Pfaff <bl*@cs.stanford.edu> writes:
Mark McIntyre <ma**********@spamcop.net> writes:

Apparently deeper than yours. I know of almost no compiler that won't
accept // comments, and several even do so when in fully compliant
mode.

That's a contradiction in terms. Support for // comments means
that a compiler is not fully compliant (to C89).


Not necessarily. A conforming C89 compiler is free to accept //
comments, as long as it issues a diagnostic for them. (Though I think
there may be some obscure cases where recognizing // as a comment
delimiter makes the difference between two different interpretations
of some code.)


Exactly, a conforming C90 compiler cannot accept // comments as doing so
would cause it to misinterpret some strictly conforming programs. The
cases aren't likely to occur in practice but that doesn't stop it being a
conformance issue. E.g.

#include <stdio.h>

int main(void)
{
printf ("%d\n", 10 //*
/**/ -2);
return 0;
}

should output -5 in C90 and 8 in C99.


Yes, that's the kind of example I was trying (and failing) to
construct. (I didn't get the trick of using "-" as both a unary and a
binary operator.)

But a conforming C90 compiler could accept // comments as long as it
checks for cases like the above. If it sees a // comment that would
make the program illegal in C90, it can issue a diagnostic and accept
it. If it sees a // comment that would not make the program illegal
in C90, it can implement the C90 semantics (with or without a
diagnostic).

I doubt that any real-world compiler bothers to do this.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #37

P: n/a
On Mon, 09 May 2005 16:00:41 -0700, in comp.lang.c , Ben Pfaff
<bl*@cs.stanford.edu> wrote:
Mark McIntyre <ma**********@spamcop.net> writes:
Apparently deeper than yours. I know of almost no compiler that won't
accept // comments, and several even do so when in fully compliant
mode.
That's a contradiction in terms.


Notice that there's a difference between a compilers 'compliant mode'
and 'being compliant '....
Support for // comments means
that a compiler is not fully compliant (to C89).


I didn't say it made sense, I just said they did it.

--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
CLC readme: <http://www.ungerhu.com/jxh/clc.welcome.txt>
Nov 14 '05 #38

P: n/a
On Tue, 10 May 2005 14:25:31 +0100, in comp.lang.c , Lawrence Kirby
<lk****@netactive.co.uk> wrote:
a conforming C90 compiler cannot accept // comments as doing so
would cause it to misinterpret some strictly conforming programs.
IMHO this isn't strictly true, since....
printf ("%d\n", 10 //*
/**/ -2);
return 0;


....the compiler can spot this sort of issue pretty easily, and could
issue a warning about ambiguity, while still issuing an informational
message the rest of the time, along the lines of

VAXC-I-PSYCHIC double-slash comments may cause ambiguities
--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
CLC readme: <http://www.ungerhu.com/jxh/clc.welcome.txt>
Nov 14 '05 #39

P: n/a
On 9 May 2005 18:28:01 -0700, in comp.lang.c , we******@gmail.com
wrote:
No. But perhaps gcc is. You need to pass it a "-std=c99" (which puts
it into this meaningless mode, where it compiles to no existing
standard) to trick it into accepting the //s.
Curious. I'm pretty sure I have a copy of the C99 standard in my
desk... yup, definitely do. So what on earth do you mean.
Remind me ... you are the one who suggested me that empty strings are
illegal right?


No, I'm the one who said that zero-length char arrays were not strings
as far as I was concerned.

--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
CLC readme: <http://www.ungerhu.com/jxh/clc.welcome.txt>
Nov 14 '05 #40

P: n/a
On 9 May 2005 19:04:24 -0700, in comp.lang.c , we******@gmail.com
wrote:
Chris Croughton wrote:
It could tell you that most people aren't interested in arguing with you
and have you in their killfiles.


Oh yeah I certainly noticed this ... you can tell by who everyone is
replying to in this thread.


There's a theory that in fact its because you are often so very badly
wrong that most regulars prefer to keep you out of the killfiles, in
order to correct your errors.
Excuse me? C++ does not expose an equivalent to "realloc()" in their
new/delete concepts.
So C++ has a flaw.
So using malloc in C++ is not wrong, its just not
generally used for base-object memory management.


No, its bad style /and/ bad programming. Not wrong though.

--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
CLC readme: <http://www.ungerhu.com/jxh/clc.welcome.txt>
Nov 14 '05 #41

P: n/a
Mark McIntyre wrote:
we******@gmail.com wrote:
No. But perhaps gcc is. You need to pass it a "-std=c99" (which putsit into this meaningless mode, where it compiles to no existing
standard) to trick it into accepting the //s.
Curious. I'm pretty sure I have a copy of the C99 standard in my
desk... yup, definitely do. So what on earth do you mean.


So your irrationality is basically total. Just reread the statement.
gcc does not compile to the C99 standard with or without the "-std=c99"
switch. But it does accept "//" if you supply it that flag which,
among other things, makes it violate C89 -- so it just puts the
compiler into some strange non-standard mode.
Remind me ... you are the one who suggested me that empty strings areillegal right?


No, I'm the one who said that zero-length char arrays were not

strings as far as I was concerned.


Right, just making sure.

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #42

P: n/a
In article <11**********************@f14g2000cwb.googlegroups .com>,
<we******@gmail.com> wrote:
gcc does not compile to the C99 standard with or without the "-std=c99"
switch. But it does accept "//" if you supply it that flag which,
among other things, makes it violate C89 -- so it just puts the
compiler into some strange non-standard mode.


?? Isn't gcc almost -always- in "some strange non-standard mode" ??
--
Beware of bugs in the above code; I have only proved it correct,
not tried it. -- Donald Knuth
Nov 14 '05 #43

P: n/a
Walter Roberson wrote:
we******@gmail.com wrote:
gcc does not compile to the C99 standard with or without the "-std=c99"switch. But it does accept "//" if you supply it that flag which,
among other things, makes it violate C89 -- so it just puts the
compiler into some strange non-standard mode.


?? Isn't gcc almost -always- in "some strange non-standard mode" ??


Well the extensions (which all compilers have, whether they claim to be
ANSI compliant or not) generally don't intrude into successful
compilation of otherwise standards compliant code. There may be a few
exceptions like invalid invasions of namespace, but its generally not a
practical impedence to real world portability.

So gcc strays from the older standards in someone minor ways (by not
rejecting "extensions" as non-compliant, possibly some namespace
invasion, and certain weird things I've noticed wrt vsnprintf() in some
older versions.) You can get somewhat closer using the "-ansi" switch.

"-std=C99" truly makes some code just plain wrong that wasn't before
(there are other posts in this thread that show that mixing up
multiplication, division, line continuation and the two kinds of
comments means that this partial support of C99 definately makes some
C89 code invalid.) If it truly did the whole C99 standard, then we
would just call this just the correct natural evolution of C to the
next standard. But it does no such thing. So trying to use gcc in
"-std=c99" mode ... other compilers don't implement the same subset of
C99, there is no specified sub-standard, and it causes some C89 code to
break that wouldn't if you left off that switch.

Realistically you would only use "-std=C99" if:

1) you are *not* assuming C99 standard support.
2) you are *not* assuming C89 standard compliance.
3) you have no or extremely limited portability concerns.
4) you need or really want to use some particular feature that it adds.

These restrictions put you in an entirely different mode of operation
versus the default which is perfectly suitable for someone programming
to the C89 standard.

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #44

P: n/a
we******@gmail.com wrote:
Richard Bos wrote:
we******@gmail.com wrote:
Al Bowers wrote:
> [...] It seems that the OP wants a pointer to the tail of the
> list for some reason unknown.

Unknown?!?! Look closely. It causes the algorithm to stop
attempting to add nodes. It is essentially a simple feedback
that lets you know that you are out of memory (or more generally
that an add_item has failed).


Which is a wrong way to handle such situations. If add_item()
fails, it should return a failure status, not fiddle with a
pointer.


Explain how this is wrong.


If you can't see without my help why scribbling over a tail pointer,
which is potentially useful elsewhere, is inferior to returning a proper
error status from a function which now returns nothing at all, I'm
afraid I can explain all I want, but you're never going to get it.
Besides, the OP's code never even checks what happens to that
pointer. If malloc() returns a null pointer, it merrily writes
through it as if nothing untoward has happened.


You might have noticed I rewrote a lot of the OP's code.


Which is completely irrelevant to what the OP himself wanted. _You_
scribble over the tail pointer; this is a bad thing, because the OP
presumably wants to use it for something.

Richard
Nov 14 '05 #45

P: n/a
Mark McIntyre <ma**********@spamcop.net> wrote:
On 9 May 2005 18:28:01 -0700, in comp.lang.c , we******@gmail.com
wrote:
No. But perhaps gcc is. You need to pass it a "-std=c99" (which puts
it into this meaningless mode, where it compiles to no existing
standard) to trick it into accepting the //s.


Curious. I'm pretty sure I have a copy of the C99 standard in my
desk... yup, definitely do. So what on earth do you mean.


Apart from that, since when does gcc in any other mode compile to an
existing Standard not of Ganuck's own making?
Remind me ... you are the one who suggested me that empty strings are
illegal right?


No, I'm the one who said that zero-length char arrays were not strings
as far as I was concerned.


No, you didn't. You said that a char array containing only a null
character is not a string. That is an at least _one_-length char array,
with space for at least the terminating null.
If you'd said that a non-existent block of no memory is not a string,
you'd have been correct. Since you claimed that a string with no content
except the terminating null is not a string ("as far as I was concerned"
is mere weaseling, since "as far as I am concerned" the moon may be made
of green cheese), you were plain wrong, since it contradicts the
Standard.

None of which contradicts the fact that you are still more reliable when
it comes to C than Mr. Hsieh, of course.

Richard
Nov 14 '05 #46

P: n/a
Xarky wrote:

Hi,

I am writing a linked list in the following way.

struct list
{
struct list *next;
char *mybuff;
};

struct list *myList = NULL;
struct list *endList = NULL;

void getline(char s[], int lim)
{
int c, i;

for(i=0; ((i<lim-1) && ((c=getchar()) != '\n')); i++)
s[i] = c;

s[i] ='\0';
} // end method getline

void add_item(char *data)
{
if (!endList)
{
endList = (struct list *)malloc(sizeof(struct list));

myList = endList;
endList->mybuff = data;
endList->next = NULL;
} // end if
else
{
endList->next = (struct list *)malloc(sizeof(struct list));
endList = endList>next;
endList->mybuff = data;
endList->next = NULL;
} // end else
}

void printList()
{
struct list *current = myList;

while(current)
{
printf("%s\n", current->mybuff);
current = current->next;
}
}

int main()
{
char buff[50];

// called for a n times
getline(buff, 50);
add_item(buff);

printList();
}

Now in the printList method, the nothing is being printed, but just a
simple blank line for each item.

Can someone help me solve this problem out.
Thanks in Advance


/* BEGIN new.c */

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

#define LINE_LEN 50
#define str(s) # s
#define xstr(s) str(s)

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

int get_line(FILE *fd, char *line);
struct list *add_item(char *string, struct list *tail);
void print_list(struct list *head);
void list_free(struct list *node, void (*free_data)(void *));

int main(void)
{
char buff[LINE_LEN + 1];
struct list *head;
struct list *tail;

puts("Enter a string. Enter an empty string to end.");
if (get_line(stdin, buff) == 1) {
head = add_item(buff, NULL);
if (head != NULL) {
tail = head;
do {
puts(
"Enter another string. "
"Enter an empty string to end."
);
} while (get_line(stdin, buff) == 1
&& (tail = add_item(buff, tail)) != NULL);
if (tail == NULL) {
fputs("\ntail equals NULL\n\n", stderr);
}
print_list(head);
list_free(head, free);
} else {
fputs("head equals NULL\n", stderr);
}
}
return 0;
}

int get_line(FILE *fd, char *line)
{
int rc = fscanf(fd, "%" xstr(LINE_LEN) "[^\n]%*[^\n]", line);

if (!feof(fd)) {
getc(fd);
}
return rc;
}

struct list *add_item(char *string, struct list *tail)
{
if (tail == NULL) {
tail = malloc(sizeof *tail);
} else {
tail -> next = malloc(sizeof *tail -> next);
tail = tail -> next;
}
if (tail != NULL) {
tail -> next = NULL;
tail -> data = malloc(strlen(string) + 1);
if (tail -> data != NULL) {
strcpy(tail -> data, string);
} else {
free(tail);
tail = NULL;
}
}
return tail;
}

void print_list(struct list *head)
{
while (head != NULL) {
printf("%s\n", (char *)head -> data);
head = head -> next;
}
}

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

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

/* END new.c */
--
pete
Nov 14 '05 #47

P: n/a
On 10 May 2005 15:48:50 -0700, in comp.lang.c , we******@gmail.com
wrote:
Mark McIntyre wrote:
we******@gmail.com wrote:
>No. But perhaps gcc is. You need to pass it a "-std=c99" (whichputs >it into this meaningless mode, where it compiles to no existing
>standard) to trick it into accepting the //s.
Curious. I'm pretty sure I have a copy of the C99 standard in my
desk... yup, definitely do. So what on earth do you mean.


So your irrationality is basically total.


Feel free to become Dan Pop's alter ego.
Just reread the statement.
gcc does not compile to the C99 standard with or without the "-std=c99"
switch.
I doubt any compiler has ever complied perfectly with any C standard,
past or present, so its futile to have a go at gcc for some
nonconformance to the present one.
But it does accept "//" if you supply it that flag which,
among other things, makes it violate C89 -- so it just puts the
compiler into some strange non-standard mode.


only to you.
--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
CLC readme: <http://www.ungerhu.com/jxh/clc.welcome.txt>

----== Posted via Newsfeeds.Com - Unlimited-Uncensored-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! 120,000+ Newsgroups
----= East and West-Coast Server Farms - Total Privacy via Encryption =----
Nov 14 '05 #48

P: n/a
(Not going to join this pointless discussion, but just one comment ...)

In article <42****************@news.xs4all.nl>, Richard Bos wrote:
Mark McIntyre <ma**********@spamcop.net> wrote:
On 9 May 2005 18:28:01 -0700, in comp.lang.c , we******@gmail.com
wrote:
>No. But perhaps gcc is. You need to pass it a "-std=c99" (which puts
>it into this meaningless mode, where it compiles to no existing
>standard) to trick it into accepting the //s.


Curious. I'm pretty sure I have a copy of the C99 standard in my
desk... yup, definitely do. So what on earth do you mean.


Apart from that, since when does gcc in any other mode compile to an
existing Standard not of Ganuck's own making?


Have you ever really used gcc? If you had, you'd know that a
combination of the ``-ansi'' (or -std=c89) and ``-pedantic'' options is
what you're looking for.

-ansi turns off GNU C extensions that aren't compatible with C89
-pedantic turns off GNU C extensions that are compatible with C89 and
turns on diagnostics that are required by C89 but omitted by GNU C

It seems like your strong feelings about this compiler (``Ganuck'') have
no technical basis.

--
Nils R. Weller, Bremen / Germany
My real email address is ``nils<at>gnulinux<dot>nl''
.... but I'm not speaking for the Software Libre Foundation!
Nov 14 '05 #49

P: n/a
Richard Bos wrote:
we******@gmail.com wrote:
Richard Bos wrote:
we******@gmail.com wrote:
> Al Bowers wrote:
> > [...] It seems that the OP wants a pointer to the tail of the
> > list for some reason unknown.
>
> Unknown?!?! Look closely. It causes the algorithm to stop
> attempting to add nodes. It is essentially a simple feedback
> that lets you know that you are out of memory (or more
> generally that an add_item has failed).

Which is a wrong way to handle such situations. If add_item()
fails, it should return a failure status, not fiddle with a
pointer.
Explain how this is wrong.


If you can't see without my help why scribbling over a tail pointer,
which is potentially useful elsewhere, is inferior to returning a
proper error status from a function which now returns nothing at
all, I'm afraid I can explain all I want, but you're never going to
get it.


The "reference to tail link" is overwritten when you are out of memory.
True it would be nice to have all sorts of O(1) assistance to deal
with whatever you are going to do about an out-of-memory situation, but
a scenario where this truly matters escapes my imagination at the
moment.

Your approach complexifies the main case in order to make dealing with
marginal cases faster. Trust me, the understanding problem is not
mine.
Besides, the OP's code never even checks what happens to that
pointer. If malloc() returns a null pointer, it merrily writes
through it as if nothing untoward has happened.


You might have noticed I rewrote a lot of the OP's code.


Which is completely irrelevant to what the OP himself wanted.


The code I wrote is plug-in compatible with what the OP wrote (without
changing his intended semantics.)
[...] _You_
scribble over the tail pointer; this is a bad thing, because the OP
presumably wants to use it for something.


He wants to use it to add_item(). I only "scribble over" the reference
to the tail link when an extremely marginal situation has occurred
which needs drastic attention and special case code to deal with. And
unlike the other suggestions posted, mine doesn't tie down the
implementation by *forcing* it to have a tail pointer.

There isn't a real sense in which the tail pointer is not available.
Think about it, suppose we were both to implement the failed malloc
scenario in our respective solutions:

1. If we decide that the list just must be destroyed and all further
processing to stop, then knowing the tail doesn't help.

2. If we decide that the upper level code should just stop adding items
and leave the list as is, and furthermore that add_item()/pop_iten()
are the only list manipulation functions, then again knowing the tail
doesn't help. My solution intrinsically stops add_item() from further
action no matter what, so you can defer the error check until the end
after your loop (and thus collect your error checking all in one
place). With your solution you must deal with the error from *INSIDE*
the inner loop.

3. If we have a way of coercing malloc to start functioning once more
(maybe we'll free memory from somewhere else or something like that)
then indeed your solution has a slight advantage. For my solution you
could copy the reference to tail link to an extra variable just before
calling add_item(), then recover it from inside the if(NULL == ...)
error case. But if you don't want to pay mainline inner loop
penalties, you can alternatively, just regenerate the reference to tail
link from scratch inside the error case (the reasoning being that this
case is so marginal that the high cost just doesn't matter.)

So I don't see that your complaint about my preference (I'm sure I'm
not the first person to have thought of this) as being seriously
legitimate. Your preference assumes the existence of a completely
artifical "tail entry" which aliases NULL with "there is no nail entry"
and requires you to handle two cases. In my solution there is *ALWAYS*
a reference to tail link -- there is no distinction between empty, one
entry or any other cases of linked lists.

Now if we add deletion into the mix, which of our solutions do you
think will be easier to test, design and implement?

---
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Nov 14 '05 #50

57 Replies

This discussion thread is closed

Replies have been disabled for this discussion.