3

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?

physics_researcher
  • 638
  • 2
  • 9
  • 22
  • Do you get the same thing with `-std=c99` or `c11`? – Mat Oct 29 '18 at 18:45
  • https://stackoverflow.com/questions/2722276/multiple-definition-of-inline-function – jxh Oct 29 '18 at 18:51
  • @Mat yes the error is still there with -std=c99 and -std=c11. – physics_researcher Oct 29 '18 at 19:02
  • 2
    See also https://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99 and https://stackoverflow.com/questions/216510/extern-inline (and to a lesser extent, https://stackoverflow.com/questions/19068705/undefined-reference-when-calling-inline-function). – Jonathan Leffler Oct 29 '18 at 19:36
  • @Bob__ that was a typo. I changed the names to make everything consistent – physics_researcher Oct 29 '18 at 20:09
  • Im dubious the book says to add the method as `extern` in a c file. I'm uncertain if it's useless or actively breaks things, but if the book actually says that, throw the book away. – Mooing Duck Oct 29 '18 at 20:25
  • 1
    @mooingduck: take a look at this example in the standard: https://port70.net/~nsz/c/c11/n1570.html#6.7.4p10. Using `extern` like that is standard-compliant. – rici Oct 29 '18 at 20:41

2 Answers2

2

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.

rici
  • 234,347
  • 28
  • 237
  • 341
2

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.)

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142