Here's an alternative approach, that should be a bit more robust. It uses C99's flexible array member.
Instead of fixed-size arrays, put the character data to the flexible array member. The first name is stored first, followed by (an end of string NUL, \0
and) the last name (and an another end of string NUL).
To avoid having to find where the first name starts, we can store either a pointer, or an offset. I prefer the offset, but as long as you (the programmer!) are careful, the pointer will work fine as well:
struct patron {
char *last_name; /* Points to within the first_name member */
char first_name[]; /* Flexible array member */
};
Usually, you write helper functions to allocate and initialize, as well as free, such structures:
void free_patron(struct patron *p)
{
if (p) {
/* "Poisoning" the structure, to help detect possible use-after-free bugs. */
p->last_name = NULL;
p->first_name[0] = '\0';
/* Both names reside in the same dynamically allocated part. */
free(p);
}
}
struct patron *new_patron(const char *first, const char *last)
{
const size_t firstlen = (first) ? strlen(first) : 0;
const size_t lastlen = (last) ? strlen(last) : 0;
struct patron *newpatron;
/* Don't allow unnamed patrons. */
if (firstlen + lastlen < 1) {
fprintf(stderr, "new_patron(): NULL or empty name.\n");
exit(EXIT_FAILURE);
}
/* Allocate enough memory for the structure. */
newpatron = malloc(sizeof (struct patron) + firstlen + 1 + lastlen + 1);
if (!newpatron) {
fprintf(stderr, "new_patron(): Not enough memory.\n");
exit(EXIT_FAILURE);
}
/* First name goes first. */
if (firstlen > 0)
memcpy(newpatron->first_name, first, firstlen);
newpatron->first_name[firstlen] = '\0';
/* Last name follows. */
newpatron->last_name = newpatron->first_name + firstlen + 1;
if (lastlen > 0)
memcpy(newpatron->last_name, last, lastlen);
newpatron->last_name[lastlen] = '\0';
return newpatron;
}
To manage an array of patrons, this time each entry is a pointer to a struct patron. This means you can choose whether you use a fixed-size array, where you locate a vacant seating by locating a NULL pointer.
struct seating {
size_t seats;
struct patron **seat;
};
#define NO_VACANCIES (~(size_t)0)
void free_seating(struct seating *s)
{
if (s) {
free(s->seat);
s->seats = 0;
s->seat = NULL;
}
}
void init_seating(struct seating *s, const size_t n)
{
size_t i;
if (!s) {
fprintf(stderr, "init_seating(): NULL pointer to struct seating.\n");
exit(EXIT_FAILURE);
}
/* No seats wanted at all? */
if (n < 1) {
s->seats = 0;
s->seat = NULL;
return;
}
s->seat = malloc(n * sizeof s->seat[0]);
if (!s->seat) {
fprintf(stderr, "init_seating(): Not enough memory.\n");
exit(EXIT_FAILURE);
}
s->seats = n;
/* Initialize all seats as vacant. */
for (i = 0; i < n; i++)
s->seat[i] = NULL;
/* Done. */
}
/* Find a vacant/unused seating.
Returns the seat index, or NO_VACANCIES if all taken. */
size_t vacant_seating(struct seating *s)
{
size_t i;
if (!s || s->seats < 1)
return NO_VACANCIES;
for (i = 0; i < s->seats; i++)
if (!s->seat[i])
return i; /* Seat i is vacant. */
return NO_VACANCIES;
}
/* Removes a patron from a seating.
You'll usually want to call
free_patron(release_seating(&my_threatre, place));
to free the structure naming the patron as well.
This is safe to do even if the seat was vacant. */
struct patron *release_seating(struct seating *s, size_t i)
{
if (s && i < s->seats) {
struct patron *old_patron = s->seat[i];
s->seat[i] = NULL;
return old_patron;
} else
return NULL;
}
In your program, using these is simple:
struct seating my_theatre;
size_t place;
/* Small venue with 50 seats. */
init_seating(&my_theatre, 50);
/* Find a vacant seat. */
place = vacant_seating(&my_theatre);
if (place == NO_VACANCIES) {
fprintf(stderr, "Sorry, the theatre is full.\n");
return EXIT_FAILURE;
}
/* Seat DanielN there. */
my_theatre.seat[place] = new_patron("Daniel", "N");
Note that because my_theatre.seat
is an array, my_theatre.seat + place
is a pointer to the place
th element in the array, exactly like &(my_theatre.seat[place])
.
Also note that when allocating arrays, say struct something *foo;
, you can use the sizeof operator: foo = malloc(n * sizeof foo[0]);
tries to allocate enough memory for n
elements of whatever type foo[0]
is. Note that to help us programmers remember that sizeof
is an operator, and not a function. Even when foo
is undefined or NULL, sizeof foo[0]
is valid, because the sizeof operator only examines the type of its argument to determine the size of the type.
The NO_VACANCIES
macro evaluates to the largest size_t
value (that the type can describe in binary on non-binary computers). That expression works for all unsigned integer types, and size_t
is an unsigned (nonnegative) integer type. It would be better to include <limits.h>
and use the SIZE_MAX
(similar to CHAR_MAX
, UCHAR_MAX
, INT_MAX
, and so on that that header file defines), but I'm not sure if all (well, Microsoft; they like to do things their own way) define SIZE_MAX
.