How do you define functions in header files?
Asked Answered
L

3

6

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?

Liebig answered 4/3, 2018 at 20:18 Comment(1)
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.Lupelupee
M
15

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

Mcclurg answered 4/3, 2018 at 20:25 Comment(7)
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.Everlasting
Also observe the rules for reserved identifiers. C11 §7.1.3 Reserved identifiers 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.Everlasting
@JonathanLeffler thanks for the precision, I might have mingled some wordsMcclurg
@JonathanLeffler wow didn't know about the Reserved identifers, in school i've learnt to write __FILE_NAME_H__Mcclurg
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.Everlasting
@Jonathan Leffler i've edited the answer so people who see the answer will be aware now :)Mcclurg
What a radical statement about functions inside header files? What about statinc inline functions in header files? Header-only libraries?Esquibel
M
10

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.

Men answered 4/3, 2018 at 20:22 Comment(1)
Bad statement about always inline. There can be static inline functions in header files. See Linux kernel code, e.g. include/linux/ip.hEsquibel
B
2

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.

Brucie answered 26/4, 2021 at 20:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.