physics_researcher
physics_researcher

Reputation: 766

inline function definition in header vs source file

I am confused about how to inline functions in c (C99 onwards). In section 18.6 of "C programming, a modern approach" (K.N. King, 2nd edition), or for example 3 in this tutorial (under "Strategies for using inline functions"), the definition of an inline function is given in the header (.h) file, then the function is again listed as extern in a source (.c) file.

For example, what I am doing: in a header "stencil.h"

#ifindef _STENCIL_H
#define _STENCIL_H

inline double D1_center_2ndOrder(double vp1, double vm1, double dr) 
{
    return (vp1-vm1) / (2.0 * dr) ;
}

#endif 

Then in a matching source file "stencil.c" one defines

#include "stencil.h"

extern double D1_center_2ndOrder(double vp1, double vm1, double dr) ;

I did this, and then in a file "main.c" I called average:

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

#include "stencil.h"

int main(int argc, char *argv[])
{
    double vp1 = 1 ; 
    double vp2 = 2 ;
    dr = 0.1 ;

    double der_v = D1_center_2ndOrder(vp1, vm1, dr) ; 
    printf("der_v\t%f\n", der_v) ;

    return 0 ; 
}

I compile everything with the following makefile

CC = gcc 

CFLAGS = -Wall -lm -std=gnu11  

OBJECTS = main.o stencil.o

DEPS_MAIN = stencil.h

test: $(OBJECTS)
    $(CC) -o collapse $(OBJECTS) $(CFLAGS)

main.o: main.c $(DEPS_MAIN)
    $(CC) -c main.c

stencil.o: stencil.c stencil.h
    $(CC) -c stencil.c

And I then get the following compiler error:

gcc  -c main.c
gcc  -c stencil.c
gcc  -o test main.o stencil.o -Wall -lm -std=gnu11 
stencil.o: In function `D1_center_2ndOrder':
stencil.c:(.text+0x0): multiple definition of `D1_center_2ndOrder'
main.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [collapse] Error 1

When I define the function in the .c source file "stencil.c" and declare it in the header file I do not get the above error. The version of gcc I am using is gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28).

My questions are:

(1) Why then does "C programming, a modern approach" and the tutorials on inlining functions that I find online suggest defining the function in the header file and listing it again as extern in a source file? Doing so for the above gives me a compiler error.

(2) When I declare the inline function in a header file then define as extern in a source file, will the compiler still inline my function?

Upvotes: 3

Views: 1479

Answers (2)

rici
rici

Reputation: 241931

You are using a portable strategy based on standard C99 (and more recent), which is entirely correct.

It is failing because you invoke gcc with its default -std setting, which for GCC 4.8.5 effectively tells it to use its legacy semantics and not standard C11 semantics. The default value for -std was gnu90 in version 4.8.5. In version 5, it was changed to gnu11, which implements C99/C11 inline semantics.

You're using default settings because your Makefile does not include $(CFLAGS) in the compile recipes; only in the final link recipe. (You can see that in the commands printed out by make).

While -std=gnu11 should work, it would be better to use -std=c11 instead. And consider upgrading to a more recent GCC version.

Upvotes: 2

Petr Skocik
Petr Skocik

Reputation: 60143

You need to pass the standard-version flag while compiling. It's not doing any good when you pass it in the linking stage.

So your makefile should be something like:

CC = gcc 

CFLAGS = -Wall -std=gnu11
LDFLAGS = -lm

OBJECTS = main.o stencil.o

DEPS_MAIN = stencil.h


test: $(OBJECTS)
    $(CC) -o collapse $(OBJECTS) $(LDFLAGS)

main.o: main.c $(DEPS_MAIN)
    $(CC) -c main.c $(CFLAGS)

stencil.o: stencil.c stencil.h
    $(CC) -c stencil.c $(CFLAGS)

I've tested your example with this shellscript:

#!/bin/sh -eu
cat > stencil.h <<EOF
#ifndef _STENCIL_H
#define _STENCIL_H

inline double D1_center_2ndOrder(double vp1, double vm1, double dr)
{
    return (vp1-vm1) / (2.0 * dr) ;
}

#endif
EOF
cat > stencil.c <<EOF
#include "stencil.h"
extern double D1_center_2ndOrder(double vp1, double vm1, double dr) ;
EOF
cat > main.c <<EOF
#include <stdio.h>
#include <stdlib.h>

#include "stencil.h"

int main(int argc, char *argv[])
{
    double vp1 = 1 ;
    double vp2 = 2 ;
    double dr = 0.1 ;
    double vm1 = 0;

    double der_v = D1_center_2ndOrder(vp1, vm1, dr) ;
    printf("der_v\t%f\n", der_v) ;

    return 0 ;
}
EOF
: ${CC:=gcc}
set -x
gcc -c main.c -std=c99
gcc -c stencil.c -std=c99
gcc -o test main.o stencil.o -lm 

and gcc 4.6.4 and it's working fine, as long as the compilations (rather than the linking command) get at least -std=c99 (It's not working with 4.6.4's defaults).

(Side note: the header guard shouldn't start with an underscore and an uppper-case letter.)

Upvotes: 2

Related Questions