5

The setup

If I have a program like this

A header file that declares my main library function, primary() and defines a short simple helper function, helper().

/* primary_header.h */
#ifndef _PRIMARY_HEADER_H
#define _PRIMARY_HEADER_H

#include <stdio.h>

/* Forward declare the primary workhorse function */
void primary();

/* Also define a helper function */
void helper()
{
    printf("I'm a helper function and I helped!\n");
}
#endif /* _PRIMARY_HEADER_H */

The implementation file for my primary function that defines it.

/* primary_impl.c */
#include "primary_header.h"
#include <stdio.h>

/* Define the primary workhorse function */
void primary()
{
    /* do the main work */
    printf("I'm the primary function, I'm doin' work.\n");

    /* also get some help from the helper function */
    helper();
}

a main() file that tests the code by calling primary()

/* main.c */
#include "primary_header.h"

int main()
{
    /* just call the primary function */
    primary();
}

The Problem

Using

gcc main.c primary_impl.c

does not link because the primary_header.h file gets included twice and therefore there is an illegal double definition of the function helper(). What is the correct way to structure the source code for this project such that double definitions do not happen?

RBF06
  • 2,013
  • 2
  • 21
  • 20
  • 5
    Don't use macros and variables names that start with underscore `_`, they are reserved. So do `#ifndef PRIMARY_HEADER_H`, etc. Please post the error message of the compiler. – Pablo Mar 04 '18 at 20:21

3 Answers3

10

You should only write your function's prototype in the header file, the body of your function should be written in a .c file.

Do this :

primary_header.h

/* primary_header.h */
#ifndef PRIMARY_HEADER_H
#define PRIMARY_HEADER_H

#include <stdio.h>

/* Forward declare the primary workhorse function */
void primary(void);

/* Also define a helper function */
void helper(void);

#endif /* PRIMARY_HEADER_H */

primary_impl.c

/* primary_impl.c */
#include "primary_header.h"
#include <stdio.h>

/* Define the primary workhorse function */
void primary()
{
    /* do the main work */
    printf("I'm the primary function, I'm doin' work.\n");

    /* also get some help from the helper function */
    helper();
}

void helper()
{
    printf("I'm a helper function and I helped!\n");
}

Edit: change _PRIMARY_HEADER_H to PRIMARY_HEADER_H. As @Jonathan Leffler and @Pablo said, underscore names are reserved identifiers

kaylum
  • 13,833
  • 2
  • 22
  • 31
Mickael B.
  • 4,755
  • 4
  • 24
  • 48
  • 1
    Note that there are no prototypes in the header. There are function declarations, but the function declarations simply say that the functions take an indeterminate argument list (but not a variable argument list like `printf()` — such functions require an explicit prototype) and do not specify the prototype. You need to write `void primary(void);` in the header to provide a prototype. For symmetry, write `void primary(void) { … }` in the source file that defines the function too. Without a prototype, the compiler can't complain if you write `primary(13, 29.6, "arbalest");` as a function call. – Jonathan Leffler Mar 04 '18 at 20:35
  • 2
    Also observe the rules for reserved identifiers. [C11 §7.1.3 Reserved identifiers](https://port70.net/~nsz/c/c11/n1570.html#7.1.3) says, in part: • _All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use._ • _All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces._ Using `_PRIMARY_HEADER_H` for the header guard violates the first of those rules — don't do it despite the number of headers you see 'out there' that do the same. – Jonathan Leffler Mar 04 '18 at 20:40
  • @JonathanLeffler thanks for the precision, I might have mingled some words – Mickael B. Mar 04 '18 at 20:41
  • @JonathanLeffler wow didn't know about the _Reserved identifers_, in school i've learnt to write `__FILE_NAME_H__` – Mickael B. Mar 04 '18 at 20:46
  • 2
    People mimic what they see in system headers, not realizing that system headers _must_ use that notation to keep out of your way, just as you _must not_ use that notation to keep out of their way. Sadly, it is common for tutors to be unaware of such minutiae — but they should be aware of them. – Jonathan Leffler Mar 04 '18 at 20:50
  • @Jonathan Leffler i've edited the answer so people who see the answer will be aware now :) – Mickael B. Mar 04 '18 at 21:02
  • What a radical statement about functions inside header files? What about `statinc inline` functions in header files? Header-only libraries? – NK-cell Aug 08 '23 at 21:28
9

You almost never write a function inside a header file unless it is marked to always be inlined. Instead, you write the function in a .c file and copy the function's declaration (not definition) to the header file so it can be used elsewhere.

C_Elegans
  • 1,113
  • 8
  • 15
  • Bad statement about always inline. There can be `static inline` functions in header files. See Linux kernel code, e.g. [include/linux/ip.h](https://github.com/torvalds/linux/blob/master/include/linux/ip.h) – NK-cell Aug 08 '23 at 22:02
1

You can define a function in header files if it's weak linkage like:

// test.h
__attribute__((weak)) int test() {
    static int s = 0;
    return s++;
}

// a.c
#include "test.h"
#include <stdio.h>
void a(){
    print("%d", test());
}

// b.c
#include "test.h"
#include <stdio.h>
void b(){
    print("%d", test());
}

// main.c
#include "test.h"
#include <stdio.h>
void a();
void b();

void main(){
    a();
    b();
    print("%d", test());
}

cc a.c b.c main.c won't raise multiple definitions error and the output should be 012 as expected, meaning a.c, b.c and main.c share the same test function. You can achieve the same result in c++ by using inline.

Moreover, weak linkage can also be used on variable definition, allowing you to define and initialize a global variable in header files without source files (similar to inline static in c++).

Note:

Weak symbols are not mentioned by the C or C++ language standards.

So be careful when using it in c. But in c++, inline and inline static are portable form c++11 and c++17.

Sy Love
  • 11
  • 1
  • 2