int CLinkedList_Output (CLinkedList *list, const char* sp) { CLinkedNode *p;
if ( list->first != NULL && list->last != NULL ) { p = list->first; while ( p != NULL) { printf ("%s %s ", p->elem, sp); p = p->next; } printf ("\n"); printf ("Have %d elements in this list\n\n", CLinkedList_Length (list)); } else { printf ("This is an empty list now\n\n"); }
return 0;
} /* End of CLinkedList_Output */
// Pop is a method used in both stack and queue with flag int CLinkedList_Pop (CLinkedList *list, int flag) { int error = 0; CLinkedNode *p = NULL;
if ( flag == C_STACK ) { if ( list->first != NULL) { p = list->first;
// If more than one elememt if ( list->first->next != NULL) { list->first = list->first->next; list->first->prev = NULL; } // If only one element else { list->first = NULL; list->last = NULL; } } else { error = C_ERR_EMPTY_LIST; goto TERMINATE; } } else if ( flag == C_QUEUE ) { if ( list->last != NULL) { p = list->last;
JC <cuiyouzhi0...@gmail.com> writes: > On Jun 18, 11:28 pm, Ben Bacarisse <ben.use...@bsb.me.uk> wrote: <snip> >> Eek! Now either *node or (*node)->elem can be NULL and both >> conditions cause trouble!
> Ah, this is a stupid bug I made. thanks. > and also, strncpy should be called like:
I can't emphasise enough: strncpy should not be called at all! It works fine as you first had it:
strncpy ((*node)->elem, elem, strlen (elem) + 1);
but it has to do more work than is needed (it must test for a null that comes only when the counter runs out and it must test the counter that runs out only at the null) and getting used to using it is a bug waiting to happen.
I have been programming in C for nearly 30 years and I last used strncpy about 29 years ago when it was exactly what was needed to copy Unix file names into directory entries because these were not null-terminated unless they were < 14 characters long (in which case you had to pack the space with nulls). I think it was invented because of this format, and I have never used it since (except in an embarrassingly erroneous posting here a few years ago). It is almost never the best function for the job: strcpy does what you want and is simpler; memcpy does it better if you have the length already.
> malloc can used like this to malloc for struct and string, > I never saw it.
This is a hack. Please be aware of that. For example, it works because char has no alignment needs. If you ever switch to wide characters this code will break.
My real point was this: allocate both or neither. You had a situation where you allocate the node and then return an error because the string could not be allocated. This is (depending on exactly how you use the result) a memory leak.
>> Of course, this is a Very Bad Design if you intend to ever pass the >> string pointer out from any list functions; or is you plan to allow >> sharing of string between lists, but you don't seem to be going that >> way.
> The better design is only give CLinkedNode **node parameter to > C_Node_Alloc_and_Init > function?
I am not sure what you mean.
>> > /* Free the list*/ >> > int >> > CLinkedList_Free (CLinkedList **list) >> > { >> > int error = 0;
> Here, I can remove oldp->elem == NULL; oldp->next = NULL;?
Yes.
> because oldp is a local variable?
No, because you are going to call free:
>> > free (oldp);
Everything that oldp pointed at has now gone. Why set something that is going to be thrown away?
>> > oldp = NULL;
> remove oldp = NULL; here too?
Yes. At the end of the loop...
>> > }
... oldp vanishes. You get a new oldp at the start of the next loop iteration (or you leave the loop and there is nothing called oldp anymore). There is no point in setting a variable to anything just before the end of its lifetime.
> but it has to do more work than is needed (it must test for a null > that comes only when the counter runs out and it must test the counter > that runs out only at the null) and getting used to using it is a bug > waiting to happen.
> I have been programming in C for nearly 30 years and I last used > strncpy about 29 years ago when it was exactly what was needed to copy > Unix file names into directory entries because these were not > null-terminated unless they were < 14 characters long (in which case > you had to pack the space with nulls). I think it was invented > because of this format, and I have never used it since (except in an > embarrassingly erroneous posting here a few years ago). It is almost > never the best function for the job: strcpy does what you want > and is simpler; memcpy does it better if you have the length already.
> > malloc can used like this to malloc for struct and string, > > I never saw it.
> This is a hack. Please be aware of that. For example, it works > because char has no alignment needs. If you ever switch to wide > characters this code will break.
> My real point was this: allocate both or neither. You had a situation > where you allocate the node and then return an error because the > string could not be allocated. This is (depending on exactly how you > use the result) a memory leak.
Yes, what I do is add free(*node) when malloc string failed.
> >> Of course, this is a Very Bad Design if you intend to ever pass the > >> string pointer out from any list functions; or is you plan to allow > >> sharing of string between lists, but you don't seem to be going that > >> way.
> > The better design is only give CLinkedNode **node parameter to > > C_Node_Alloc_and_Init > > function?
> I am not sure what you mean.
What I said here is the design of CNode_Alloc_and_Init function. the current version is : static int CNode_Alloc_and_Init (CLinkedNode **node, const char* elem) but you said it is a bad design, in fact, I didn't understand where it is bad.
What you prefer is, I think, static int CNode_Alloc_and_Init (CLinkedNode **node) to let elem NULL and alloc/init in list functions?
JC <cuiyouzhi0...@gmail.com> writes: > On Jun 19, 1:02 am, Ben Bacarisse <ben.use...@bsb.me.uk> wrote: <snip> > Yes, what I do is add free(*node) when malloc string failed.
>> >> Of course, this is a Very Bad Design if you intend to ever pass the >> >> string pointer out from any list functions; or is you plan to allow >> >> sharing of string between lists, but you don't seem to be going that >> >> way.
>> > The better design is only give CLinkedNode **node parameter to >> > C_Node_Alloc_and_Init >> > function?
>> I am not sure what you mean.
> What I said here is the design of CNode_Alloc_and_Init function. > the current version is : > static int > CNode_Alloc_and_Init (CLinkedNode **node, > const char* elem) > but you said it is a bad design, in fact, I didn't understand where it > is bad.
Ah! What I meant was my design (allocating both the node and the string together) was bad is some cases. Not yours.
Dear Ben and friends, Thanks for your suggestions about my code. Refer to your ideas, I post the latest version of linkedlist here. and also, I wrote a arraylist, also post here. Welcome your suggestion!!!
1. include files: file struct{ C.h constant.h errormap.h
static ErrorMap errorMap[] = { {C_ERR_NO_MEMORY, "No memory can be used"}, {C_ERR_NOTVALID_INDEX, "Not valid index"}, {C_ERR_NOT_FOUND, "Element not found in list"}, {C_ERR_EMPTY_LIST, "Empty list"}, {C_ERR_NOT_STACK_AND_QUEUE, "Pop not in stack and queue"},
};
#endif --------end--------
2. sort (only select sort and bubble sort in this version) file struct { sort.h sort.c
}
--------sort.h-------- #include "../include/constant.h" void CBubble_Sort(C_TYPE *a, int len); void CSelect_Sort(C_TYPE *a, int len); --------end-------- --------sort.c-------- #include "sort.h"
void CSelect_Sort(C_TYPE *a, int len) { int i, j, min; C_TYPE t;
for (i = 0; i < len; ++i) { min = i; for (j = i + 1; j < len; ++j) { if (a[j] < a[min]) { min = j; } }
if ( i != min ) { t = a[i]; a[i] = a[min]; a[min] = t; } }
} /* End of CSelect_Sort*/
void CBubble_Sort(C_TYPE *a, int len) { int i, j; C_TYPE t;
for (i = 0; i < len; ++i) { for (j = i + 1; j < len; ++j) { if (a[j] < a[i]) { t = a[i]; a[i] = a[j]; a[j] = t; } } }
--------seqlist.h-------- #ifndef H_SEQLIST #define H_SEQLIST #include "../include/C.h" typedef struct { C_TYPE *elem_p; /* The basic address of list */ int length; /* The length of list */ int capacity; /* The capacity of list*/
} CSeqList;
int CSeqList_Init(CSeqList **list); int CSeqList_Free(CSeqList **list); int CSeqList_Clear(CSeqList *list); int CSeqList_Insert(CSeqList *list, int index, const C_TYPE elem); int CSeqList_Delete(CSeqList *list, int index, C_TYPE* e); int CSeqList_Output(CSeqList *list); int CSeqList_Sort(CSeqList *list, int length); int CSeqList_Find(CSeqList *list, const C_TYPE elem, int *index); #endif --------end-----------
--------seqlist.c-------- /* sequence list by John Cui*/ #include "seqlist.h" #include "../sort/sort.h"
int CSeqList_Init (CSeqList **list) { int error = 0;
//move elements which is after list[index] to next p = &(list->elem_p[index]); for (q = &(list->elem_p[list->length - 1]); q >= p; --q) { *(q+1) = *q; }
//insert elem and increase the length of list *p = elem; ++list->length;
TERMINATE:
return error;
} /* END of CSeqList_Insert */
int CSeqList_Delete (CSeqList *list, int index, C_TYPE *e) { int error = 0;
C_TYPE *p = NULL; C_TYPE *q = NULL;
//check index if ( index < 0 || index > list->length ) { error = C_ERR_NOTVALID_INDEX; goto TERMINATE; }
//keep the deleted element e = &(list->elem_p[index]);
//move the element which after list[list] to previous p = &list->elem_p[index]; for (q = p; q <= &(list->elem_p[list->length-1]); ++q) { *q = *(q+1); } --list->length;
int CLinkedList_Init(CLinkedList **list); int CLinkedList_Free(CLinkedList **list); int CLinkedList_Append(CLinkedList *list, const char* elem); int CLinkedList_Insert(CLinkedList *list, int pos, const char* elem); int CLinkedList_Pop(CLinkedList *list, int flag);
JC <cuiyouzhi0...@gmail.com> wrote: > On Jun 18, 11:02=A0pm, ralt...@xs4all.nl (Richard Bos) wrote: > > In theory, yes. In practice, since you _will_ use strncpy() with strings > > where the lengths don't add up so nicely and aren't known in advance in > > the first place, use strncat() instead. > yes, I found your method is better.
So why do you ignore it?
> and if dest is a dynamic char* in my function CNode_Alloc_and_Init, > we should get the length of source first, then malloc my char* elem > field, at this time, the strncpy should be written:
_NO!_ Never use strncpy(), unless you're messing about with the internal structures of whatever benighted version of Unix that function was invented for. If you may or may not have enough room for the entire string you're copying, still _do not_ use strncpy(), use strncat() instead. In your case, though, you know that you either have enough memory, or no memory at all. In that case, simply use strcpy().
Utterly bogus. You cast malloc(), if it returns a null pointer you merrily clobber through it, and you still use strncpy(). _This_ is correct (at least, as far as only that fragment is concerned):
On Jun 24, 3:03 am, ralt...@xs4all.nl (Richard Bos) wrote:
> JC <cuiyouzhi0...@gmail.com> wrote: > > On Jun 18, 11:02=A0pm, ralt...@xs4all.nl (Richard Bos) wrote: > > > In theory, yes. In practice, since you _will_ use strncpy() with strings > > > where the lengths don't add up so nicely and aren't known in advance in > > > the first place, use strncat() instead. > > yes, I found your method is better.
> So why do you ignore it?
I have changed it in the latest version(the last posted message). Could you please review my code in the last post message and give me some suggestions?
> Utterly bogus. You cast malloc(), if it returns a null pointer you > merrily clobber through it, and you still use strncpy(). _This_ is > correct (at least, as far as only that fragment is concerned):
> > Utterly bogus. You cast malloc(), if it returns a null pointer you > > merrily clobber through it, and you still use strncpy(). _This_ is > > correct (at least, as far as only that fragment is concerned):
> > _Forget_ strncpy() completely. It will never be the right function for > > you to use.
> Thanks, and do you mean we should _Forget_ convert the return value of > malloc when we allocate memory?
he said "forget strncpy" NOT "_Forget_ convert the return value of malloc when we allocate memory?". There is no need to explicitly convert the return value of malloc as an implicit conversion is done. void* is automatically converted to any other pointer type as necessary. So the cast (a) means more characters to type and (b) may hide an error (as I remarked some time ago...)
-- Nick Keighley
Casting is almost always wrong, and the places where it's right are rarely the places you'd guess. Richard Heathfield (that really *was* the next quote in the quote file)
> > Thanks, and do you mean we should _Forget_ convert the return value of > > malloc when we allocate memory?
> he said "forget strncpy" NOT "_Forget_ convert the return value of > malloc when we allocate memory?". There is no need to explicitly > convert the return value of malloc as an implicit conversion is done. > void* is automatically converted to any other pointer type as > necessary. > So the cast (a) means more characters to type and (b) may hide > an error (as I remarked some time ago...)
malloc return void* which can be converted automatically to any other pointer type, then, when we use malloc function and assign the return value to any pointer don't need explicitly convert.
So, I think we can _forget_ convert the return value of malloc for any type pointer like: ANY_TYPE_POINTER p = malloc(...);
In article <b8fde4fc-ff0d-4ee8-8e0e-3e6385808...@l35g2000pra.googlegroups.com>,
JC <cuiyouzhi0...@gmail.com> wrote: >Dear all, >I wrote the following function to read and filter "malloc" from file >to mylist. >Is there anywhere to improve?