Steve Zimmerman said:
This program compiles fine, but are there any hidden dangers
in it, or is it ok?
Experiment 1 ##################################################
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
Huh? This isn't a standard header. Besides, the prototype for
malloc() is in stdlib.c anyway.
#include <string.h>
int main()
{
struct pres {
char name[25];
struct pres *next;
};
struct pres *president;
president = (struct pres *)malloc(sizeof(struct pres));
Don't cast the return value of malloc in C. First of all, you don't
need to; malloc() returns a void*, which is implicitly converted to
the target pointer type. Secondly, doing so will suppress a
diagnostic if you forget to include stdlib.h or otherwise don't have a
prototype for malloc() in scope.
*ALWAYS* check the return value of malloc() before attempting to use
it.
BTW, you can use sizeof *president instead of sizeof (struct pres) as
an argument to malloc(). That way, if the base type of president ever
changes, you don't have to hack all your calls to malloc().
strcpy(president->name, "George Washington");
It's a good idea to protect against potential buffer overruns; don't
just blindly assume that your target is always big enough to hold the
string you're copying. Even though you *know* that it is in this
particular case, it's a good idea to put in sanity checks anyway.
president->next = (struct pres *)malloc(sizeof(struct pres));
Same rant as above re: casting the return value of malloc().
printf("The first structure has been created:\n");
printf("president->name = %s\n", president->name);
printf("next structure address = %i\n", president->next);
Use the %p conversion specifier to print pointer values, not %i.
return 0;
}
###################################################################
--Steve
I know you're just experimenting, but when you create abstract data
types like this, it's a good idea to create an abstract interface for
them as well. Instead of calling malloc() and strcpy() directly from
the application code, create a set of functions that encapsulate all
the operations necessary to create and manage objects of that type.
For example (off the top of my head, untested, all the usual caveats
apply):
struct pres *newPresident (char *name)
{
struct pres *p;
p = malloc (sizeof *p);
if (p)
{
p->next = NULL;
if (name)
{
if (strlen (name) > sizeof p->name - 1)
printf ("Name too long -- truncating...\n");
strncpy (p->name, name, sizeof p->name - 1);
p->name[sizeof p->name - 1] = 0;
}
}
return p;
}
void addToList (struct pres *p, struct pres *head)
{
struct pres *cur;
assert (head != NULL);
cur = head;
while (cur->next)
cur = cur->next;
cur->next = p;
}
void printEntry (struct pres *p)
{
printf ("addr = %p\nname = %s\nnext = %p\n\n",
p, p != NULL ? p->name : "(null)",
p != NULL ? p->next : "(null)");
}
void printList (struct pres *head)
{
struct pres *cur = head->next;
while (cur)
{
printEntry (cur);
cur = cur->next;
}
}
And a very crude example of these functions in use:
int main (void)
{
struct pres head, *newp;
newp = newPresident ("George Washington");
if (newp)
addToList (newp, &head);
newp = newPresident ("Abraham Lincoln");
if (newp)
addToList (newp, &head);
newp = newPresident ("James K. Polk");
if (newp)
addToList (newp, &head);
printList (&head);
return 0;
}