0

I am not sure what is the best practice for including multiple c files. I was given two header files for an assignment, and am now required to create the corresponding c files.

Right now my issue is that I need to use functions defined in course.c in student.c, however, I am running into a linker issue that I am not sure how to resolve. The way I am currently including files is by creating multiple function definitions!

student.h

#include <stdbool.h>
#include <stdint.h>


struct course;

struct student;

struct student_id {
    uint16_t    sid_year;
    uint32_t    sid_serial;
};

struct student* student_create(struct student_id, bool grad_student);

void        student_free(struct student*);

void        student_take(struct student *s, struct course*, uint8_t grade);

int     student_grade(struct student*, struct course*);

double      student_passed_average(const struct student*);

bool        student_promotable(const struct student*);

student.c

#include "student.h"
#include "course.c"

struct student
{
    <Fields>
};

struct student* student_create(struct student_id id, bool grad_student)
{
    <definition>
}

void        student_free(struct student* s)
{
    <definition>
}

void        student_take(struct student *s, struct course* c, uint8_t grade)
{
    <definition>
}

int     student_grade(struct student* s, struct course* c)
{
    <definition>
}

double      student_passed_average(const struct student* s)
{
    <definition>
}

bool        student_promotable(const struct student* s)
{
    <definition>
}

course.h

#include <stdint.h>


/** Course subjects. */
enum subject {
    SUBJ_ENGI,
    SUBJ_CIV,
    SUBJ_ECE,
    SUBJ_MECH,
    SUBJ_ONAE,
    SUBJ_PROC,
    SUBJ_CHEM,
    SUBJ_ENGL,
    SUBJ_MATH,
    SUBJ_PHYS,
};

struct course;

struct course*  course_create(enum subject, uint16_t code);

enum subject    course_subject(const struct course*);

uint16_t    course_code(const struct course*);

void        course_hold(struct course*);

void        course_release(struct course*);

int     course_refcount(const struct course*);

course.c

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

struct course {
    <fields>
};

struct course*  course_create(enum subject sub, uint16_t code)
{
    <definition>
}

enum subject course_subject(const struct course* c)
{
    <definition>
}

uint16_t    course_code(const struct course* c)
{
    <definition>
}

void        course_hold(struct course* c)
{
    <definition>
}

void        course_release(struct course* c)
{
    <definition>
}

int     course_refcount(const struct course* c)
{
    <definition>
}

Which gives the following errors:

gcc *

multiple definition of `course_create'; ... first defined here ...
multiple definition of `course_subject'; ... first defined here ...
multiple definition of `course_code'; ... first defined here ...
multiple definition of `course_hold'; ... first defined here ...
multiple definition of `course_release'; ... first defined here ...
multiple definition of `ref_count'; ... first defined here ...

I am assuming this is an issue with me including course.c in student.c, but I need to include course.c to get the course structure definition. So I am wondering how I can go about retrieving the definition of struct course without using the line #include course.c

Jarrod Boone
  • 53
  • 1
  • 6
  • 4
    Never `#include` C source files. You always include the header. If you need direct access to the elements of a `struct` then that definition should be in the header – UnholySheep Jan 22 '23 at 19:56
  • 2
    `I need to include course.c to get the course structure definition` no, you don't – Iłya Bursov Jan 22 '23 at 19:57
  • Each `.c` file gets compiled to a corresponding `.o` file. Then you link all the `.o` files together. – Barmar Jan 22 '23 at 19:57
  • Don't place executable code in a header file. Just declarations and macros. – Weather Vane Jan 22 '23 at 19:59
  • @AviBerger Not really. My issue is I need to access the fields from the `struct course* c` pointers in the student.c file. If in the student.c file, I change `#include "course.c"` to `#include "course.h"` , I get a new type of error: `pointer to incomplete class type "struct course" is not allowed`. My issue is I need to be able to access the `struct course*` fields through the course pointer in the student.c file. – Jarrod Boone Jan 22 '23 at 20:31
  • You need to define the course structure inside course.h rather than course.c. – SafelyFast Jan 22 '23 at 20:55
  • 1) You include course.h, not course.c in student.c. 2) You correct course.h (and any other .h files) to use correct [include guards](https://stackoverflow.com/q/1653958/631266) 3) You move the definition of `struct course` from course.c to course.h. ( Where it belongs unless you were using it as an opaque type and _never_ accessing its members outside of course.c and never creating an object of that type directly or dynamically except in course.c) 4) You then try to sort out whatever other error messages you have. – Avi Berger Jan 22 '23 at 21:03

1 Answers1

0

Do not #include source files. Move the structure definitions from the source files to the appropriate header files. Use include guards to avoid header files being processed more than once.

Below is a complete example that will compile cleanly with

gcc course.c student.c main.c

This is essentially an application of the answer to How to split a C program into multiple files?, but with functions and structures.

course.h:

#ifndef COURSE_H
#define COURSE_H

#include <stdint.h>

enum subject {
    SUBJ_ENGI,
    SUBJ_CIV,
    SUBJ_ECE,
    SUBJ_MECH,
    SUBJ_ONAE,
    SUBJ_PROC,
    SUBJ_CHEM,
    SUBJ_ENGL,
    SUBJ_MATH,
    SUBJ_PHYS,
};

struct course {
    enum subject subject;
};

struct course *course_create(enum subject, uint16_t);
enum subject course_subject(const struct course *);
uint16_t course_code(const struct course *);
void course_hold(struct course *);
void course_release(struct course *);
int course_refcount(const struct course *);

#endif

student.h:

#ifndef STUDENT_H
#define STUDENT_H

#include <stdbool.h>
#include <stdint.h>

#include "course.h"

struct student {
    char name[128];
};

struct student_id {
    uint16_t    sid_year;
    uint32_t    sid_serial;
};

struct student *student_create(struct student_id, bool);
void student_free(struct student *);
void student_take(struct student *, struct course *, uint8_t);
int student_grade(struct student *, struct course *);
double student_passed_average(const struct student *);
bool student_promotable(const struct student *);

#endif

course.c:

#include <stddef.h>
#include <stdint.h>

#include "course.h"

struct course *course_create(enum subject sub, uint16_t code)
{
    return NULL;
}

enum subject course_subject(const struct course *c)
{
    return SUBJ_ENGI;
}

uint16_t course_code(const struct course *c)
{
    return 0;
}

void course_hold(struct course *c)
{
}

void course_release(struct course *c)
{
}

int course_refcount(const struct course *c)
{
    return 0;
}

student.c:

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "course.h"
#include "student.h"

struct student *student_create(struct student_id id, bool grad_student)
{
    return NULL;
}

void student_free(struct student *s)
{
}

void student_take(struct student *s, struct course *c, uint8_t grade)
{
}

int student_grade(struct student *s, struct course *c)
{
    return 0;
}

double student_passed_average(const struct student *s)
{
    return 0.0;
}

bool student_promotable(const struct student *s)
{
    return false;
}

main.c:

#include <stdio.h>

#include "course.h"
#include "student.h"

int main(void)
{
    struct student_id id = { 0, 0 };
    struct student *stu = student_create(id, 0);
    struct course *cour = course_create(SUBJ_MATH, 0);

    puts("Hello world.");
}
Oka
  • 23,367
  • 6
  • 42
  • 53