Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
240 views
in Technique[技术] by (71.8m points)

makefile - Breaking up large C program in Header files and C files

I have to break up the following code into the following files: main.c, student.c, students.h, mergesort.c, mergesort.h, aux.c and aux.h. I then have to make a makefile to compile it all. The program is a mergesort implemented on a linked list. I've separated up the code, but I have no clue as to what to do in terms of headers files and include directives, and even less of a clue as to how to create the makefile. What do I need to include for the headers files, and what do I need to include in the C files? As an example, if mergesort.c is using functions from students.c, would I have to include students.h in mergesort.c? Here's the code for the original program:

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

#define NAME_LEN 25

struct node {
  int number;
  char name[NAME_LEN+1];
  struct node* next;
};

/* The functions to manage the linked list.  The functions prompt the
   user and read the standard input if needed. */
struct node* insert        (struct node* student_list);
void         print_student (struct node* student);
void         print_list    (struct node* student_list);
void         search        (struct node* student_list);
struct node* delete        (struct node* student_list);
void         delete_list   (struct node* student_list);

/* Merge sort */
struct node* mergesort(struct node* student_list);
struct node* merge    (struct node* list1, struct node *list2);

                                     /* Auxiliary functions */
int read_line(char line[], int len); /* Read at most len characters
                    from the standard input and
                    ignore the rest of the line. */
int line_skip(); /* Read the standard input to the end of the line. */
int line_copy(); /* Read the standard input to the end of the line
            and copy to the standard output. */
int pause();     /* Ask user to press Enter to continue. */

int main() {
  int option;
  struct node* student_list = NULL;

  for (;;) {
    printf("
-- OPTIONS MENU -----------------
");
    printf("1: Add a student
");
    printf("2: Search for a student by number
");
    printf("3: Delete a student by number
");
    printf("4: Display all students
");
    printf("5: Sort students by number
");
    printf("0: Exit
");
    printf("
");

    printf("Enter an option: ");
    if ( scanf("%d", &option) != 1 ) {
      if ( feof(stdin) ) break;
      printf("Invalid option: "); line_copy(); pause();
      continue;
    }

    /* Read the rest of the line after option number.  Usually, it is
       just one new-line character */
    line_skip(); 

    if (option == 0) break;

    switch(option) {
    case 1: student_list = insert(student_list);    break;
    case 2: search(student_list);                   break;
    case 3: student_list = delete(student_list);    break;
    case 4: print_list(student_list);               break;
    case 5: student_list = mergesort(student_list); break;
    default:
      printf("Incorrect option: %d
", option); pause();
    }
  }

  delete_list(student_list); /* Not necessary in this example */
  printf("Bye!
");
  return 0;
}

struct node* mergesort(struct node* student_list) {
  struct node* list1 = student_list;
  struct node* list2 = student_list;

  if (student_list == NULL || student_list->next == NULL)
    return student_list;

  while ((list2 = list2->next) != NULL && 
         (list2 = list2->next) != NULL)
    list1 = list1->next;

  list2 = list1->next;

  list1->next = NULL ;
  list1 = student_list;

  list1 = mergesort(list1);
  list2 = mergesort(list2);

  return merge(list1, list2);
}

struct node* merge(struct node* list1, struct node* list2) {
  struct node *list, *prev;

  if (list1 == NULL) return list2;
  if (list2 == NULL) return list1;

  if (list1->number <= list2->number) {
    list = list1; list1 = list1->next;
  } else {
    list = list2; list2 = list2->next;
  }

  prev = list;

  while (list1 != NULL && list2 != NULL) {
    if (list1->number <= list2->number) {
      prev->next = list1;
      list1 = list1->next;
    } else {
      prev->next = list2;
      list2 = list2->next;
    }
    prev = prev->next ;
  }

  if (list1 != NULL)
    prev->next = list1;
  else
    prev->next = list2;

  return list;
}

struct node* insert(struct node* student_list) {
  struct node* student = malloc(sizeof(struct node));
  /* Why would it be incorrect to use "struct node student;" ? */

  if (student == NULL ) {
    printf("Out of memory for a new student!
"); pause();
    return student_list;
  }

  printf("
Adding a new student
");
  printf("Enter student's number: ");
  if (scanf("%d", &student->number) != 1) {
    printf("Incorrect student number: ");
    line_copy(); pause();
    free(student); /**/
    return student_list;
  }
  line_skip();          /* to skip the newline character */

  printf("Enter student's name: ");
  read_line(student->name, NAME_LEN);

  student->next = student_list;
  printf("Student %d added.
", student->number); pause();

  return student;
}

void print_student(struct node* student) {
  printf("Number:%3d  Name: %s
", student->number, student->name);
}

void print_list(struct node* student_list) {
  printf("
Student List:
");
  while (student_list != NULL) {
    print_student(student_list);
    student_list = student_list->next;
  }
  pause();
}

void search(struct node* student_list) {
  int number;

  printf("Enter student number: ");
  if (scanf("%d", &number) != 1) {
    printf("Incorrect student number: ");
    line_copy(); pause();
    return;
  }
  line_skip();

  while (student_list != NULL && number != student_list->number) 
    student_list = student_list->next;

  if (student_list == NULL)
    printf("Not found.
");
  else
    print_student(student_list);
  pause();
}

struct node* delete(struct node* student_list) {
  int number;
  struct node *prev, *cur;

  printf("Enter student number: ");
  if (scanf("%d", &number) != 1) {
    printf("Incorrect student number: "); line_copy(); pause();
    return student_list;
  }
  line_skip();

  for (cur = student_list, prev = NULL;
       cur != NULL && cur -> number != number;
       prev = cur, cur = cur->next)
    ;

  if (cur == NULL) {
    printf("Student not found!
"); pause();
    return student_list;
  }

  if (prev == NULL)
    student_list = student_list->next;
  else
    prev->next = cur->next;

  free(cur);
  return student_list;
}

void delete_list(struct node* student_list) {
  struct node* temp;

  while (student_list != NULL) {
    temp = student_list;
    student_list = student_list->next;
    free(temp);
  }
}

/*Auxiliary Function
int read_line(char line[], int len) {
  int ch, i = 0;

  while ((ch = getchar()) != '
' && ch != EOF) 
    if (i < len) 
      line[i++] = ch;

  line[i] = '';

  return i;
}

int line_skip() {
  int ch;
  while ( (ch=getchar()) != '
' && ch != EOF )
    ;
  return ch != EOF;
}

int line_copy() {
  int ch;
  while ( (ch=getchar()) != '
' && ch != EOF )
    putchar(ch);
  putchar('
');
  return ch != EOF;
}

int pause() {
  printf("Press Enter to continue...");
  return line_skip();
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Your headers will contain the type definitions and function declarations for the relevant sections of code. Note that if the user code (primarily main.c) only calls mergesort() and not merge(), then the mergesort.h header should only declare mergesort() and merge() should be a static function in mergesort.c, hidden from the rest of the code. A header should only define what 'clients' need to know; implementation details should be kept hidden. Remember to ensure that the headers are self-contained (so if mergesort.h needs to know about struct node, it includes the header that declares struct node, for example). Also ensure they are idempotent (so writing #include "header.h" twice won't cause compilation errors). This is done with header guards such as:

#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED

…body of header file…

#endif /* HEADER_H_INCLUDED */

The source files will contain the implementations of the functions declared in the headers. The source files will include the relevant headers. Parts of the code that don't need to know about a given structure type shouldn't need to include the header that declares that structure type.

The outline makefile can be simple:

FILES.c = main.c student.c mergesort.c aux.c
FILES.o = ${FILES.c:.c=.o}

all: students

students: ${FILES.o}
    ${CC} ${CFLAGS} -o $@ ${FILES.o} ${LDFLAGS} ${LDLIBS}

students.o:  students.h
mergesort.o: mergesort.h
aux.o:       aux.h

Since make knows how to build xyz.o from xyz.c, you don't need to specify those dependencies. You should declare the headers used by main.c (so you need a line such as main.o: students.h mergesort.h aux.h, but you've not indicated what's correct).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...