C Pointers

J

Justin Spahr-Summers

Tor Rustad wrote:
(e-mail address removed) wrote:
Yes, assuming argv[0] represent the program name, I have yet to see an
environment where this isn't the case. Anybody knows of one?
Not specifically but I would guess that in environments where the C
program is the only program on the device, it would be meaningless for
it to have a name.

when your program consists of source files which are dynamically loaded and
compiled, there is no meaningful name to pass to main...

argv[0] is always present. C99 draft N1256 (section 5.1.2.2.1):
"If the value of argc is greater than zero, the string pointed to by
argv[0] represents the program name; argv[0][0] shall be the null
character if the program name is not available from the host
environment. If the value of argc is greater than one, the strings
pointed to by argv[1] through argv[argc-1] represent the program
parameters."
 
C

CBFalconer

Martien said:
.... snip ...

Also, the input is an array, not a linked list, and a good
assumption would be that the array should be sorted in place, or
at least that the output should be a sorted array. Translating it
into a linked list, sort, and then translating back, is by no
means simpler than just using a method that acts directly on the
data structure at hand.

I did NOT recommend converting arrays to lists. I recommended (and
showed) using lists to accumulate the input data, sorting it, and
dumping it. No gyrations about array size.
 
C

CBFalconer

Tor said:
.... snip ...

Yes, assuming argv[0] represent the program name, I have yet to
see an environment where this isn't the case. Anybody knows of one?

Virtually any embedded program.
 
C

cr88192

Justin Spahr-Summers said:
Tor Rustad wrote:
(e-mail address removed) wrote:
Yes, assuming argv[0] represent the program name, I have yet to see an
environment where this isn't the case. Anybody knows of one?
Not specifically but I would guess that in environments where the C
program is the only program on the device, it would be meaningless for
it to have a name.

when your program consists of source files which are dynamically loaded
and
compiled, there is no meaningful name to pass to main...

argv[0] is always present. C99 draft N1256 (section 5.1.2.2.1):
"If the value of argc is greater than zero, the string pointed to by
argv[0] represents the program name; argv[0][0] shall be the null
character if the program name is not available from the host
environment. If the value of argc is greater than one, the strings
pointed to by argv[1] through argv[argc-1] represent the program
parameters."

yes, but is "\0" sensibly a valid program name?...
I will say it is not, and thus the above statements hold...

so, argv[1] to argv[argc-1] represent the program args, but argv[0] is not
necessarily the program name...
 
C

cr88192

CBFalconer said:
I did NOT recommend converting arrays to lists. I recommended (and
showed) using lists to accumulate the input data, sorting it, and
dumping it. No gyrations about array size.

I usually just use in-place sorts.
implementing an in-place quicksort and in certain edge cases falling back to
selection or bubble sort seems to hold up fairly well (usually, sel sort can
be made very small, and is thus easy to type, and is only very rarely
invoked anyways).

sel-sort example:
if(depth>=24)
{
for(i=0; i<cnt; i++)for(j=i+1; j<cnt; j++)
if(val[j]<val)swap(val, i, j);
return;
}

bubble sort is often better, but often also takes a few more lines of code.
'swap' is already needed if implementing an in-place quicksort anyways.


usually the only time this happens is in some rare cases when one is faced
with some dismally-bad inputs (oddly, these often seem to be just the sort
of inputs that turn out fairly well with bubble sort...).

if(depth>=24)
{
i=1; b=0; c=cnt;
while(i)
{
i=0; for(j=b; j<(c-1); j++)
if(val[j]>val[j+1]) { swap(val, j, j+1); i++; }
c--; for(j=c-1; j>b; j--)
if(val[j]<val[j-1]) { swap(val, j, j-1); i++; }
b++;
}
return;
}

or such...
 
T

Tor Rustad

CBFalconer said:
Tor Rustad wrote:
.... snip ...
Yes, assuming argv[0] represent the program name, I have yet to
see an environment where this isn't the case. Anybody knows of one?

Virtually any embedded program.

For some reason, I was talking about "hosted environment". :)
 
W

Walter Roberson

Tor Rustad said:
Yes, assuming argv[0] represent the program name, I have yet to see an
environment where this isn't the case. Anybody knows of one?

Sure. POSIX offers a couple of exec*() calls in which argv[0]
can be completely arbitrary, and that facility is actually used
at times. For example,

$ ps -fe
[...]
nobody 597 451 0 Sep 21 ? 1:43 (pinger)
nobody 539 451 0 Sep 21 ? 0:04 (unlinkd)
roberson 21333 21332 0 Nov 29 ? 0:00 (dns helper)

The last of those is a process forked off by Netscape.


Historically in BSD, writing to argv[0] changed what was reported
by ps, a facility taken advantage of to indicate program status.
 
G

Gordon Burditt

Yes, assuming argv[0] represent the program name, I have yet to see an
environment where this isn't the case. Anybody knows of one?

I don't know of any hosted environment where argv[0] is one of the
normal command-line arguments as distinguished from NULL or some
other string representing something else.

However, there are a variety of things you might get for a "program
name".

Nobody said the program name is unique. It could be the name of a
symlink or hard link (which *some* systems have but not all) pointed
at the executable. Some programs deliberately take advantage of
this to use the same executable for multiple functions.

Even if it *is* a file name of the executable, nobody said it was
a full path or what it is relative to. Different UNIX shells may
vary on this point.

UNIX permits setting argv[0] arbitrarily and independently of the
file path of the program. Setting argv[0] to something misleading
maliciously is trivial if you can call exec*(). To follow the
convention, you have to go out of your way calling the exec*()
functions to ensure that the two strings are the same.

The standard doesn't guarantee that the program name is a file name
of a piece of the program. It might be a store catalog name (e.g.
what you order when you want to buy it "Microsoft Office 2007
Copyright 1989-2007 Microsoft Corporation"). It might be the name
of a program (a sub-part of an organization, e.g. the Solar
Colonization Program of NASA) that wrote this software. I haven't
actually seen any system doing this, but it's possible. And because
it's easy to supply an arbitrary value of argv[0] using the UNIX/POSIX
exec*() call, anyone could do it.
 
P

pete

CBFalconer said:
Merge sort code is simple. Input is simple, because you just
extend a linked list by one for each input line. No problem as
long as memory holds out.

Here is suitable stuff for the merge sort.

/* List handling, reversal, sort, merge, split */
/* file listops.h */
/* By C.B. Falconer. Public Domain, use as you wish */
/* Attribution appreciated. */
/* ================== 30 April, 2001 ================== */

#ifndef listops_h_
#define listops_h_

#include <stddef.h> /* NULL */
#include <iso646.h> /* not, and */

/* The bare minimum to form a linked list */
typedef struct node {
struct node *next;
void *data;
} node, *nodeptr;

/* ======================================================= */
/* believed necessary and sufficient for NULL terminations */
/* Reverse a singly linked list. Reentrant (pure) code */
nodeptr revlist(nodeptr root);

/* ======================================================= */
/* split list p into 2 nearly equal lists, return 2nd part */
nodeptr splitlist(nodeptr p);

/* ================================ */
/* Merge two ordered lists into one */
nodeptr mergelists(nodeptr p1, nodeptr p2,
int (*cmp)(void *, void*)); /* compare */

I think that

nodeptr mergelists(nodeptr p1, nodeptr p2,
int (*cmp)(nodeptr, nodeptr)); /* compare */

would make more sense.
 
C

CBFalconer

pete said:
CBFalconer wrote:
.... snip ...


I think that

nodeptr mergelists(nodeptr p1, nodeptr p2,
int (*cmp)(nodeptr, nodeptr)); /* compare */

would make more sense.

No, because nodeptr->data can point to any collection of data, and
you may want to sort based on some particular facet of that data.
The void* allows the user to customize as needed.
 
F

Flash Gordon

Tor Rustad wrote, On 02/12/07 01:47:
CBFalconer said:
Tor Rustad wrote:
.... snip ...
Yes, assuming argv[0] represent the program name, I have yet to
see an environment where this isn't the case. Anybody knows of one?

Virtually any embedded program.

For some reason, I was talking about "hosted environment". :)

Any Unix like system if the process execing your program decides to lie
about the name of the program it is invoking. Look up the Posix exec*
functions to see what I mean.
 
H

Harald van Dijk

=?UTF-8?q?Harald_van_D=C4=B3k?= said:
=?UTF-8?q?Harald_van_D=C4=B3k?= wrote:
On Dec 1, 4:30 am, (e-mail address removed) wrote:
How do I sort an string array using pointers

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

static int
cmpstringp(const void *p1, const void *p2) {
/* The actual arguments to this function are "pointers to
pointers to char",
but strcmp() arguments are "pointers to char", hence the
following cast plus dereference */
return strcmp(* (char * const *) p1, * (char * const *) p2);

The type of your strcmp arguments is (char *const). The strcmp
parameter type is (const char *).

char * can be implicitly converted to const char *.

This way would be more better:

return strcmp(* (const char **) p1, * (const char **) p2);

argv is declared as char *[].
argv[n] (which is what p1 and p2 point to) is a char *. You
shouldn't
access a char * as if it were a const char *.

I'm not saying it's not allowed,
because I'm not sure about that, but I consider it poor style at
least.

However,
strcmp *will* access its arguments as type (const char *).

No, it won't.
It will access its parameters as type (const char *), and its
parameters *are* type const char *, as the result of the implicit
conversion from the arguments (of type char *) to const char *.

I can't parse out which effect you say occurs:
"... as the result of the implicit conversion from the arguments
(of type char *) to const char *."

char *x = "a";
char *y = "b";
strcmp(x, y);

The arguments are x and y. x and y are of type char *. The arguments are
implicitly converted to the type of the parameters as declared in
strcmp's function prototype.
At least in the bad example, your code acknowledges that the purpose of
the cast is to convert the value to the type of the object receiving the
value.

The whole point of casting a pointer initializer, is to convert the
value
to the type of the object receiving the value.

Recording the type history of the value, is not the purpose of the cast.

But you weren't casting the pointer value argv[n], you were
reinterpreting its bit pattern, by casting a pointer to argv[n]. That's
what the second example does (except for the actual dereference). That's
what you shouldn't do.

static int
cmpstringp(const void *p1, const void *p2) {
return strcmp(* (char * const *) p1, * (char * const *) p2);
}

cmpstringp will be called by qsort as, for example,
cmpstringp(&argv[1], &argv[2]);

argv[n] is of type char *. So p1 and p2 are pointers to a char *. So they
should be dereferenced as pointers to char *. If you want to get a
const char *, you can convert the value _after_ dereferencing.
 
P

pete

CBFalconer said:
No, because nodeptr->data can point to any collection of data, and
you may want to sort based on some particular facet of that data.
The void* allows the user to customize as needed.

No, because the arguments to your mergelists compar function
will always be pointers to nodes,
so it doesn't make sense to pretend
that the arguments to the compar function can be anything else.

The void* doesn't do anything to enhance the ability
of the user to customize as needed.

To rewrite any of these below shown compar functions
with (void *) parameters instead of nodeptr parameters,
would require an extra cast to the structure type pointer,
to make (a -> data) out to be a pointer to a structure type.

int lu_comp(nodeptr a, nodeptr b)
{
const long unsigned a_num = *(long unsigned *)(a -> data);
const long unsigned b_num = *(long unsigned *)(b -> data);

return b_num > a_num ? -1 : b_num != a_num;
}
int lu_comp_v(void *a, void *b)
{
const long unsigned a_num = *(long unsigned *)(((nodeptr)a)->data);
const long unsigned b_num = *(long unsigned *)(((nodeptr)b)->data);

return b_num > a_num ? -1 : b_num != a_num;
}


int numcomp(nodeptr a, nodeptr b)
{
const long unsigned a_num = strtoul(a -> data, NULL, 10);
const long unsigned b_num = strtoul(b -> data, NULL, 10);

return b_num > a_num ? -1 : b_num != a_num;
}
int numcomp_v(void *a, void *b)
{
const long unsigned a_num = strtoul(((nodeptr)a)->data, NULL, 10);
const long unsigned b_num = strtoul(((nodeptr)b)->data, NULL, 10);

return b_num > a_num ? -1 : b_num != a_num;
}


int lencomp(nodeptr a, nodeptr b)
{
const size_t a_len = strlen(a -> data);
const size_t b_len = strlen(b -> data);

return b_len > a_len ? -1 : b_len != a_len;
}
int lencomp_v(void *a, void *b)
{
const size_t a_len = strlen(((nodeptr)a) -> data);
const size_t b_len = strlen(((nodeptr)b) -> data);

return b_len > a_len ? -1 : b_len != a_len;
}
 
P

pete

=?UTF-8?q?Harald_van_D=C4=B3k?= said:
=?UTF-8?q?Harald_van_D=C4=B3k?= said:
On Dec 1, 4:30 am, (e-mail address removed) wrote:
How do I sort an string array using pointers

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

static int
cmpstringp(const void *p1, const void *p2) {
/* The actual arguments to this function are "pointers to
pointers to char",
but strcmp() arguments are "pointers to char",
hence the
following cast plus dereference */
return strcmp(* (char * const *) p1,
* (char * const *) p2);

The type of your strcmp arguments is (char *const). The strcmp
parameter type is (const char *).

char * can be implicitly converted to const char *.

This way would be more better:

return strcmp(* (const char **) p1, * (const char **) p2);

argv is declared as char *[].
argv[n] (which is what p1 and p2 point to) is a char *. You
shouldn't
access a char * as if it were a const char *.

I'm not saying it's not allowed,
because I'm not sure about that, but I consider it poor style at
least.

However,
strcmp *will* access its arguments as type (const char *).

No, it won't.
It will access its parameters as type (const char *), and its
parameters *are* type const char *, as the result of the implicit
conversion from the arguments (of type char *) to const char *.

I can't parse out which effect you say occurs:
"... as the result of the implicit conversion from the arguments
(of type char *) to const char *."

char *x = "a";
char *y = "b";
strcmp(x, y);

The arguments are x and y. x and y are of type char *.
The arguments are
implicitly converted to the type of the parameters as declared in
strcmp's function prototype.
At least in the bad example,
your code acknowledges that the purpose of
the cast is to convert the value to the type
of the object receiving the value.

The whole point of casting a pointer initializer, is to convert the
value
to the type of the object receiving the value.

Recording the type history of the value,
is not the purpose of the cast.

But you weren't casting the pointer value argv[n], you were
reinterpreting its bit pattern, by casting a pointer to argv[n].
That's
what the second example does
(except for the actual dereference). That's
what you shouldn't do.

static int
cmpstringp(const void *p1, const void *p2) {
return strcmp(* (char * const *) p1, * (char * const *) p2);
}

cmpstringp will be called by qsort as, for example,
cmpstringp(&argv[1], &argv[2]);

argv[n] is of type char *.
So p1 and p2 are pointers to a char *. So they
should be dereferenced as pointers to char *. If you want to get a
const char *, you can convert the value _after_ dereferencing.

OK.
I'm thinking about it now.
Thank you.
 
C

CBFalconer

pete said:
No, because the arguments to your mergelists compar function
will always be pointers to nodes, so it doesn't make sense to pretend
that the arguments to the compar function can be anything else.

Not the arguments, but what is passed to, for example, strcmp. For
example, data may point to a structure containing two pointers to
strings. The cmp function can select which of those strings get
compared. By simply passing a different cmp function we can sort
the same list in different ways.
 
P

pete

CBFalconer said:
Not the arguments, but what is passed to, for example, strcmp. For
example, data may point to a structure containing two pointers to
strings. The cmp function can select which of those strings get
compared. By simply passing a different cmp function we can sort
the same list in different ways.

The various cmp functions are simpler to write
when they have nodeptr parameters,
than they are when they have (void *) parameters.

To sort a list of long unsigned with (void *) cmp parameters,
takes a function like this:

int lu_comp_vp(const void * a, const void * b)
{
const long unsigned a_lu = *(long unsigned *)(((nodeptr)a) -> data);
const long unsigned b_lu = *(long unsigned *)(((nodeptr)b) -> data);

return b_lu > a_lu ? -1 : b_lu != a_lu;
}

To sort a list of long unsigned with nodeptr cmp parameters,
takes a function like this:

int lu_comp_np(const nodeptr a, const nodeptr b)
{
const long unsigned a_lu = *(long unsigned *)(a -> data);
const long unsigned b_lu = *(long unsigned *)(b -> data);

return b_lu > a_lu ? -1 : b_lu != a_lu;
}

It's just simpler code with the nodeptr parameters.

I wrote two programs: NPlist_sort and VPlist_sort.
NP and VP stand for NodePointer and VoidPointer.
The programs both do exactly the same thing.
They each sort a list of strings and
also sort a list of long unsigneds using
the same sorting function with different cmp functions.
The only difference in the two programs is the cmp parameters.
The cmp function for comparing strings is simpler
in the program with nodeptr parameters
and the cmp function for comparing long unsigneds
is simpler in the program with nodeptr parameters.

It's just

int lencomp_vp(const void * a, const void * b)
{
const size_t a_len = strlen(((nodeptr)a) -> data);
const size_t b_len = strlen(((nodeptr)b) -> data);

return b_len > a_len ? -1 : b_len != a_len;
}
int lu_comp_vp(const void * a, const void * b)
{
const long unsigned a_lu = *(long unsigned *)(((nodeptr)a) ->data);
const long unsigned b_lu = *(long unsigned *)(((nodeptr)b) ->data);

return b_lu > a_lu ? -1 : b_lu != a_lu;
}
nodeptr mergelists_vp(nodeptr p1, nodeptr p2,
int (*cmp)(const void *, const void *));
nodeptr mergesort_vp(nodeptr root,
int (*cmp)(const void *, const void *));

versus

int lencomp_np(const nodeptr a, const nodeptr b)
{
const size_t a_len = strlen(a -> data);
const size_t b_len = strlen(b -> data);

return b_len > a_len ? -1 : b_len != a_len;
}
int lu_comp_np(const nodeptr a, const nodeptr b)
{
const long unsigned a_lu = *(long unsigned *)(a -> data);
const long unsigned b_lu = *(long unsigned *)(b -> data);

return b_lu > a_lu ? -1 : b_lu != a_lu;
}
nodeptr mergelists_np(nodeptr p1, nodeptr p2,
int (*cmp)(const nodeptr, const nodeptr));
nodeptr mergesort_np(nodeptr root,
int (*cmp)(const nodeptr, const nodeptr));

There is no other difference in the programs.

This is the output:

/* BEGIN VPlist_sort.c output */

Original order of list of pointers to strings:
one
two
three
four
five
six
seven
eight
nine
ten

List after stable mergesort_vp by string length:
one
two
six
ten
four
five
nine
three
seven
eight

Original order of list of pointers to long unsigned:
15
14
13
7
20
9
8
12
11
6

List after mergesort_vp:
6
7
8
9
11
12
13
14
15
20

/* END VPlist_sort.c output */

/* BEGIN VPlist_sort.c */

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

#define NMEMB(A) (sizeof (A) / sizeof *(A))

typedef struct node {
struct node *next;
void *data;
} node, *nodeptr;

int lencomp_vp(const void * a, const void * b);
int lu_comp_vp(const void * a, const void * b);
nodeptr mergelists_vp(nodeptr p1, nodeptr p2,
int (*cmp)(const void *, const void *));
nodeptr mergesort_vp(nodeptr root,
int (*cmp)(const void *, const void *));

nodeptr revlist(nodeptr root);
nodeptr splitlist(nodeptr p);
void list_free(nodeptr knowed, void (*free_data)(void *));
nodeptr list_push(nodeptr head, void *data, size_t size);
int list_fputs(nodeptr knowed, FILE *stream);
int list_fprintf_lu(nodeptr knowed, FILE *stream);

int main(void)
{
nodeptr head;
nodeptr temp;
char *string[] = {
"one","two","three","four","five",
"six","seven","eight","nine","ten"
};
char **const after = string + NMEMB(string);
char **ptr;
long unsigned lu_numbers[] = {
15,14,13,7,20,9,8,12,11,6
};
long unsigned *const lu_after = lu_numbers + NMEMB(lu_numbers);
long unsigned *lu_ptr;

head = NULL;
for (ptr = string; ptr != after; ++ptr) {
temp = list_push(head, *ptr, strlen(*ptr) + 1);
if (temp == NULL) {
list_free(head, free);
head = NULL;
puts("malloc trouble!");
break;
}
head = temp;
}
head = revlist(head);
puts("/* BEGIN VPlist_sort.c output */");
puts("\nOriginal order of list of pointers to strings:");
list_fputs(head, stdout);
puts("\nList after stable mergesort_vp by string length:");
head = mergesort_vp(head, lencomp_vp);
list_fputs(head, stdout);
list_free(head, free);
puts("\nOriginal order of list of pointers to long unsigned:");
head = NULL;
for (lu_ptr = lu_numbers; lu_ptr != lu_after; ++lu_ptr) {
temp = list_push(head, lu_ptr, sizeof *lu_ptr);
if (temp == NULL) {
list_free(head, free);
head = NULL;
puts("malloc trouble!");
break;
}
head = temp;
}
head = revlist(head);
list_fprintf_lu(head, stdout);
puts("\nList after mergesort_vp:");
head = mergesort_vp(head, lu_comp_vp);
list_fprintf_lu(head, stdout);
list_free(head, free);
puts("\n/* END VPlist_sort.c output */");
return 0;
}

int lencomp_vp(const void * a, const void * b)
{
const size_t a_len = strlen(((nodeptr)a) -> data);
const size_t b_len = strlen(((nodeptr)b) -> data);

return b_len > a_len ? -1 : b_len != a_len;
}

int lu_comp_vp(const void * a, const void * b)
{
const long unsigned a_lu = *(long unsigned *)(((nodeptr)a) -> data);
const long unsigned b_lu = *(long unsigned *)(((nodeptr)b) -> data);

return b_lu > a_lu ? -1 : b_lu != a_lu;
}

int list_fprintf_lu(nodeptr knowed, FILE *stream)
{
int rc;

while (knowed != NULL) {
rc = fprintf(stream, "%lu\n",
*(long unsigned *)(knowed -> data));
if (0 > rc) {
break;
}
knowed = knowed -> next;
}
return rc;
}

void list_free(nodeptr knowed, void (*free_data)(void *))
{
nodeptr next_node;

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

int list_fputs(nodeptr knowed, FILE *stream)
{
while (knowed != NULL) {
if (fputs(knowed -> data, stream) == EOF) {
return EOF;
}
if (putc('\n', stream) == EOF) {
return EOF;
}
knowed = knowed -> next;
}
return '\n';
}

nodeptr revlist(nodeptr root)
{
nodeptr 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 */

nodeptr list_push(nodeptr head, void *data, size_t size)
{
nodeptr knowed;

knowed = malloc(sizeof *knowed);
if (knowed != NULL) {
knowed -> next = head;
knowed -> data = malloc(size);
if (knowed -> data != NULL) {
memcpy(knowed -> data, data, size);
} else {
free(knowed);
knowed = NULL;
}
}
return knowed;
}

nodeptr splitlist(nodeptr p)
{
nodeptr p1, p2, p3;

p1 = p2 = p3 = p;
if (! p) return NULL;
do {
p3 = p2;
p2 = p2->next; /* advance 1 */
p1 = p1->next;
if (p1) p1 = p1->next; /* advance 2 */
} while (p1);

/* now form new list after p2 */
p3->next = NULL; /* terminate 1st half */
return p2;
} /* splitlist */

nodeptr mergelists_vp(nodeptr p1, nodeptr p2,
int (*cmp)(const void *, const void *))
{
node n;
nodeptr p;

p = &n;
n.next = p;

while (p1 && p2) {
if (cmp(p1, p2) <= 0) {
p->next = p1; p = p1; p1 = p1->next;
}
else {
p->next = p2; p = p2; p2 = p2->next;
}
}
/* at least one list now empty, copy other */
/* one or both of these operations is null */
if (p1) p->next = p1;
if (p2) p->next = p2;

/* check for empty lists */
if (n.next == &n) return NULL;
return n.next;
} /* mergelists_vp */

nodeptr mergesort_vp(nodeptr root,
int (*cmp)(const void *, const void *))
{
nodeptr p;

if (root && root->next) { /* 2 up items in list */
p = splitlist(root); /* alters list root */
root = mergelists_vp(mergesort_vp(root, cmp),
mergesort_vp( p, cmp),
cmp);
}
/* else the unit or empty list is already sorted */

return root;
} /* mergesort_vp */

/* END VPlist_sort.c */



/* BEGIN NPlist_sort.c */

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

#define NMEMB(A) (sizeof (A) / sizeof *(A))

typedef struct node {
struct node *next;
void *data;
} node, *nodeptr;

int lencomp_np(const nodeptr a, const nodeptr b);
int lu_comp_np(const nodeptr a, const nodeptr b);
nodeptr mergelists_np(nodeptr p1, nodeptr p2,
int (*cmp)(const nodeptr, const nodeptr));
nodeptr mergesort_np(nodeptr root,
int (*cmp)(const nodeptr, const nodeptr));
nodeptr revlist(nodeptr root);
nodeptr splitlist(nodeptr p);
void list_free(nodeptr knowed, void (*free_data)(void *));
nodeptr list_push(nodeptr head, void *data, size_t size);
int list_fputs(nodeptr knowed, FILE *stream);
int list_fprintf_lu(nodeptr node, FILE *stream);

int main(void)
{
nodeptr head;
nodeptr temp;
char *string[] = {
"one","two","three","four","five",
"six","seven","eight","nine","ten"
};
char **const after = string + NMEMB(string);
char **ptr;
long unsigned lu_numbers[] = {
15,14,13,7,20,9,8,12,11,6
};
long unsigned *const lu_after = lu_numbers + NMEMB(lu_numbers);
long unsigned *lu_ptr;

head = NULL;
for (ptr = string; ptr != after; ++ptr) {
temp = list_push(head, *ptr, strlen(*ptr) + 1);
if (temp == NULL) {
list_free(head, free);
head = NULL;
puts("malloc trouble!");
break;
}
head = temp;
}
head = revlist(head);
puts("/* BEGIN NPlist_sort.c output */");
puts("\nOriginal order of list of pointers to strings:");
list_fputs(head, stdout);
puts("\nList after stable mergesort_np by string length:");
head = mergesort_np(head, lencomp_np);
list_fputs(head, stdout);
list_free(head, free);
puts("\nOriginal order of list of pointers to long unsigned:");
head = NULL;
for (lu_ptr = lu_numbers; lu_ptr != lu_after; ++lu_ptr) {
temp = list_push(head, lu_ptr, sizeof *lu_ptr);
if (temp == NULL) {
list_free(head, free);
head = NULL;
puts("malloc trouble!");
break;
}
head = temp;
}
head = revlist(head);
list_fprintf_lu(head, stdout);
puts("\nList after mergesort_np:");
head = mergesort_np(head, lu_comp_np);
list_fprintf_lu(head, stdout);
list_free(head, free);
puts("\n/* END NPlist_sort.c output */");
return 0;
}

int lencomp_np(const nodeptr a, const nodeptr b)
{
const size_t a_len = strlen(a -> data);
const size_t b_len = strlen(b -> data);

return b_len > a_len ? -1 : b_len != a_len;
}

int lu_comp_np(const nodeptr a, const nodeptr b)
{
const long unsigned a_lu = *(long unsigned *)(a -> data);
const long unsigned b_lu = *(long unsigned *)(b -> data);

return b_lu > a_lu ? -1 : b_lu != a_lu;
}

int list_fprintf_lu(nodeptr knowed, FILE *stream)
{
int rc;

while (knowed != NULL) {
rc = fprintf(stream, "%lu\n",
*(long unsigned *)(knowed -> data));
if (0 > rc) {
break;
}
knowed = knowed -> next;
}
return rc;
}

void list_free(nodeptr knowed, void (*free_data)(void *))
{
nodeptr next_node;

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

int list_fputs(nodeptr knowed, FILE *stream)
{
while (knowed != NULL) {
if (fputs(knowed -> data, stream) == EOF) {
return EOF;
}
if (putc('\n', stream) == EOF) {
return EOF;
}
knowed = knowed -> next;
}
return '\n';
}

nodeptr revlist(nodeptr root)
{
nodeptr 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 */

nodeptr list_push(nodeptr head, void *data, size_t size)
{
nodeptr knowed;

knowed = malloc(sizeof *knowed);
if (knowed != NULL) {
knowed -> next = head;
knowed -> data = malloc(size);
if (knowed -> data != NULL) {
memcpy(knowed -> data, data, size);
} else {
free(knowed);
knowed = NULL;
}
}
return knowed;
}

nodeptr splitlist(nodeptr p)
{
nodeptr p1, p2, p3;

p1 = p2 = p3 = p;
if (! p) return NULL;
do {
p3 = p2;
p2 = p2->next; /* advance 1 */
p1 = p1->next;
if (p1) p1 = p1->next; /* advance 2 */
} while (p1);

/* now form new list after p2 */
p3->next = NULL; /* terminate 1st half */
return p2;
} /* splitlist */

nodeptr mergelists_np(nodeptr p1, nodeptr p2,
int (*cmp)(const nodeptr, const nodeptr)) /* compare
*/
{
node n;
nodeptr p;

p = &n;
n.next = p;

while (p1 && p2) {
if (cmp(p1, p2) <= 0) {
p->next = p1; p = p1; p1 = p1->next;
}
else {
p->next = p2; p = p2; p2 = p2->next;
}
}
/* at least one list now empty, copy other */
/* one or both of these operations is null */
if (p1) p->next = p1;
if (p2) p->next = p2;

/* check for empty lists */
if (n.next == &n) return NULL;
return n.next;
} /* mergelists_np */

nodeptr mergesort_np(nodeptr root,
int (*cmp)(const nodeptr, const nodeptr))
{
nodeptr p;

if (root && root->next) { /* 2 up items in list */
p = splitlist(root); /* alters list root */
root = mergelists_np(mergesort_np(root, cmp),
mergesort_np( p, cmp),
cmp);
}
/* else the unit or empty list is already sorted */

return root;
} /* mergesort_np */

/* END NPlist_sort.c */
 
C

CBFalconer

pete said:
The various cmp functions are simpler to write when they have
nodeptr parameters, than they are when they have (void *) parameters.

The only thing affected is the cmp function, and now the void*
parameters make the central block (that manipulates the list,
including sorting) much simpler and a constant library entry. The
only thing needed in the actual cmp function is something like:

int acmp(const void *litem, const void* ritem) {
const whatever ldptr = litem, rdptr = ritem;

... code using ldptr and rdptr ...
}

Remember that the actual cmp code is in the users code area. The
list processing, sorting, etc. code couldn't care less what is in
the data fields. Note how I have eliminated great gobs of
inter-related code.
 
P

pete

CBFalconer said:
The only thing affected is the cmp function, and now the void*
parameters make the central block (that manipulates the list,
including sorting) much simpler and a constant library entry. The
only thing needed in the actual cmp function is something like:

int acmp(const void *litem, const void* ritem) {
const whatever ldptr = litem, rdptr = ritem;

... code using ldptr and rdptr ...
}

That's the point.
With nodeptr parameters instead of void * parameters,
the equivalent cmp function becomes:

int acmp(const nodeptr litem, const nodeptr ritem) {
... code using litem and ritem ...
}

.... which is a simpler way to write it.
 
P

pete

pete said:
CBFalconer wrote:

It's not "const whatever",
it's always going to be "const nodeptr".

The way that your mergesort and mergelist functions
call the cmp function,
the argument is always going to be a nodeptr value,
and the only way that the cmp function can use that value
is as a nodeptr value.
 
P

pete

pete said:
It's not "const whatever",
it's always going to be "const nodeptr".

Because the code using ldptr and rdptr ...,
is always going to be (ldptr -> data) and (rdptr -> data).
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top