I am trying to teach myself C from a python background. My current mini-problem is trying to do less hard-coding of things like array lengths and allocate memory dynamically based on input.
I've written the following program. I was hoping for suggestions from the community for modifying it in the following ways:
1.) Make first and last Name elements variable length. Currently their length is hardcoded as MAX_NAME_LENGTH.  This will involve both change Names structdeclaration as well as the way I'm assigning values to its elements.
2.) Bonus: Figure out some way to progressively add new elements to the name_list array without having to determine its length beforehand.  Basically make it an expandable list.
/* namelist.c 
   Loads up a list of names from a file to then do something with them.
*/
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#define DATAFILE "name_list.txt"
#define DATAFILE_FORMAT "%[^,]%*c%[^\n]%*c"
#define MAX_NAME_LENGTH 100
typedef struct {
  char first[MAX_NAME_LENGTH];
  char last[MAX_NAME_LENGTH];
} Name;
int main() {
  FILE *fp = fopen(DATAFILE, "r");
  // Get the number of names in DATAFILE at runtime.
  Name aName;
  int lc = 0;
  while ((fscanf(fp, DATAFILE_FORMAT, aName.last, aName.first))!=EOF) lc++;
  Name *name_list[lc];
  // Now actually pull the data out of the file
  rewind(fp);
  int n = 0;
  while ((fscanf(fp, DATAFILE_FORMAT, aName.last, aName.first))!=EOF)
  {
    Name *newName = malloc(sizeof(Name));
    if (newName == NULL) {
      puts("Warning: Was not able to allocate memory for ``Name`` ``newName``on the heap.");
    } 
    memcpy(newName, &aName, sizeof(Name));
  name_list[n] = newName;
  n++;
  }
  int i = 1;
  for (--n; n >= 0; n--, i++) {
    printf("%d: %s %s\n", i, name_list[n]->first, name_list[n]->last);
    free(name_list[n]);
    name_list[n] = NULL;
  }
  fclose(fp);
  return 0;
}
Sample contents of name_list.txt:
Washington,George
Adams,John 
Jefferson,Thomas
Madison,James
Update 1:
I've implemented a linked list and some helper functions as @Williham suggested, results are below.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DATAFILE "name_list.txt"
#define MAX_NAME_LENGTH 30
#define DATAFILE_FORMAT "%29[^,]%*c%29[^\n]%*c"
static const int INPUT_BUFFER_SIZE_DEFAULT = sizeof(char) * MAX_NAME_LENGTH;
typedef struct _Name Name;
struct _Name {
  char *first;
  char *last;
  Name *next;
};
int get_charcount(char *str);
Name * create_name_list(char *filename);
void print_name_list(Name *name);
void free_name_list (Name *name);
int main() {
  // Read a list of names into memory and 
  // return the head of the linked list.
  Name *head = create_name_list(DATAFILE);
  // Now do something with all this data.
  print_name_list(head);
  // If you love something, let it go.
  free_name_list(head);
  head = NULL;
  return 0;
}
int get_charcount (char *str) 
{
  int input_length = 1;
    while (str[input_length] != '\0')
    {
      input_length++;
    }
  return input_length;
}
Name * create_name_list(char *filename)
{
  FILE *fp = fopen(DATAFILE, "r");
  char *first_input_buffer = malloc(INPUT_BUFFER_SIZE_DEFAULT);
  char *last_input_buffer = malloc(INPUT_BUFFER_SIZE_DEFAULT);
  Name *firstNamePtr;
  Name *previousNamePtr;
  while ((fscanf(fp, DATAFILE_FORMAT, last_input_buffer, first_input_buffer))!=EOF)
  {
    Name *newNamePtr = malloc(sizeof(Name));
    if (previousNamePtr) 
    {
      previousNamePtr->next = newNamePtr;
      previousNamePtr = newNamePtr;
    } 
    else 
    {
      firstNamePtr = previousNamePtr = newNamePtr;
    }
    char *temp_buffer = malloc(get_charcount(first_input_buffer));
    strcpy(temp_buffer, first_input_buffer);
    newNamePtr->first = malloc(get_charcount(first_input_buffer));
    strcpy(newNamePtr->first, temp_buffer);
    realloc(temp_buffer, get_charcount(last_input_buffer));
    strcpy(temp_buffer, last_input_buffer);
    newNamePtr->last = malloc(get_charcount(last_input_buffer));
    strcpy(newNamePtr->last, temp_buffer);
    free(temp_buffer);
    temp_buffer = NULL;
  }
  previousNamePtr->next = NULL;
  previousNamePtr = NULL;
  free(first_input_buffer);
  free(last_input_buffer);
  first_input_buffer = NULL;
  last_input_buffer = NULL;
  fclose(fp);
  return firstNamePtr;
}
void print_name_list (Name *name)
{
  static int first_iteration = 1;
  if (first_iteration) 
  {
    printf("\nList of Names\n");
    printf("=============\n");
    first_iteration--;
  }
  printf("%s %s\n",name->first, name->last);
  if (name->next)
    print_name_list(name->next);
  else
    printf("\n");
}
void free_name_list (Name *name)
{
  if (name->next)
    free_name_list(name->next);
  free(name->first);
  free(name->last);
  name->first = NULL;
  name->last = NULL;
  name->next = NULL;
  free(name);
  name = NULL;
}
 
     
     
     
     
     
    