You have a number of options available to you to take into consideration. First, since you are reading lines-of-input containing the names, using a line-oriented input function like fgets
makes a lot of sense. Then you only have two considerations to check regarding end of input, (1) fgets
returns NULL
indicating EOF
or (2), you check the first character of the buffer filled by fgets
to see if it is the '\n'
character (indicating that Enter alone has been pressed.
Next you need to consider what data structure you will use to hold the array of names until you have gathered all the input. You can use an array of struct
as shown in the other answer, or you can simply use a *pointer-to-pointer-to-char` (a double-pointer) to allocate pointers, as needed, and assign the address for an allocated block of memory containing each name.
The procedure is straight forward. First allocate some reasonably anticipated number of pointers to work with. Read names, allocate storage for each, copy the name to the allocated memory and then assign the memory address to each pointer. When you reach the limit of your currently allocated pointers, simply realloc
additional pointers and keep going.
Once all the names have been collected, you simply output the names (I have included the pointer index as well) and then free
the memory of each name and then after all names are printed and freed, free
the pointers and you are done. You can do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXP = 32, MAXC = 256 }; /* constants */
void *xcalloc (size_t nmemb, size_t size); /* helper functions */
void *xrealloc (void *ptr, size_t psz, size_t *nelem, size_t inc);
int main (void) {
size_t max = MAXP, n = 0; /* initial ptrs and counter */
char **a = xcalloc (max, sizeof *a); /* mem for MAXP ptrs */
for (;;) { /* loop until empty line or EOF */
size_t len = 0;
char buf[MAXC] = "";
/* read into buf from stdin, check if empty */
if (!fgets (buf, MAXC, stdin) || *buf == '\n')
break;
len = strlen (buf); /* get length of buf */
if (buf[len-1] == '\n') /* test for '\n' */
buf[--len] = 0; /* remove '\n' */
a[n] = xcalloc (len + 1, 1); /* mem to hold buf */
strcpy (a[n++], buf); /* copy buf to a[n] */
if (n + 1 == max) /* ptr limit reached, realloc ptr
* note: +1 insures a sentinel pointer
* with bits zeroed as the last pointer */
a = xrealloc (a, sizeof *a, &max, MAXP);
}
for (size_t i = 0; i < n; i++) { /* print and free mem */
printf ("a[%3zd] : %s\n", i, a[i]);
free (a[i]); /* free memory allocated for each name */
}
free (a); /* free pointers */
return 0;
}
/* simple calloc with error-checking */
void *xcalloc (size_t nmemb, size_t size)
{
void *memptr = calloc (nmemb, size);
if (!memptr) {
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
return memptr;
}
/* simple realloc with error-checking */
void *xrealloc (void *ptr, size_t psz, size_t *nelem, size_t inc)
{
void *memptr = realloc ((char *)ptr, (*nelem + inc) * psz);
if (!memptr) {
fprintf (stderr, "realloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
} /* zero new memory (optional) */
memset ((char *)memptr + *nelem * psz, 0, inc * psz);
*nelem += inc; /* update number of elements allocated */
return memptr;
}
Above a static buffer of 256
characters was used to read each name from stdin
. If for any reason that may not be enough, you can always validate a complete line was read by adding a check for the length and checking for the trailing '\n'
.
Example Use/Output
$ ./bin/readprn <dat/namesfirst.txt
a[ 0] : adam
a[ 1] : allice
a[ 2] : barny
a[ 3] : beauregard
a[ 4] : bell
a[ 5] : benjamin
a[ 6] : betty
a[ 7] : bill
a[ 8] : buck
a[ 9] : buddy
a[ 10] : candice
a[ 11] : caset
a[ 12] : cathy
a[ 13] : charles
a[ 14] : chris
a[ 15] : chuck
a[ 16] : clark
a[ 17] : david
a[ 18] : don
a[ 19] : eli
a[ 20] : elizabeth
a[ 21] : emma
a[ 22] : eric
a[ 23] : ester
a[ 24] : frank
The code behaves the same if you manually type each name and then press Enter alone on the final line. (I just happened to have a names file)
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/readprn <dat/namesfirst.txt
==28428== Memcheck, a memory error detector
==28428== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28428== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28428== Command: ./bin/readprn
==28428==
a[ 0] : adam
<snip>
a[ 24] : frank
==28428==
==28428== HEAP SUMMARY:
==28428== in use at exit: 0 bytes in 0 blocks
==28428== total heap usage: 26 allocs, 26 frees, 413 bytes allocated
==28428==
==28428== All heap blocks were freed -- no leaks are possible
==28428==
==28428== For counts of detected and suppressed errors, rerun with: -v
==28428== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.