header 与源文件中的内联函数定义

inline function definition in header vs source file

我对如何在 c(C99 及更高版本)中内联函数感到困惑。在"C programming, a modern approach"(K.N.King,第2版)的第18.6节中,或者this教程中的示例3(在"Strategies for using inline functions"下),给出了内联函数的定义在 header (.h) 文件中,该函数在源 (.c) 文件中再次列为 extern。

比如我在做什么:在一个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 

然后在一个匹配的源文件中"stencil.c"一个定义

#include "stencil.h"

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

我这样做了,然后在一个文件中 "main.c" 我调用了平均值:

#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 ; 
}

我用下面的 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

然后我得到以下编译器错误:

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

当我在 .c 源文件 "stencil.c" 中定义函数并在 header 文件中声明它时,我没有收到上述错误。我使用的 gcc 版本是 gcc (GCC) 4.8.5 20150623(红帽 4.8.5-28)。

我的问题是:

(1) 为什么 "C programming, a modern approach" 和我在网上找到的关于内联函数的教程建议在 header 文件中定义函数并在源文件中再次将其列为 extern ?这样做会给我一个编译器错误。

(2) 当我在 header 文件中声明内联函数然后在源文件中定义为 extern 时,编译器是否仍会内联我的函数?

您使用的是基于标准 C99(及更新版本)的可移植策略,这是完全正确的。

失败是因为您使用默认的 -std 设置调用 gcc,这对于 GCC 4.8.5 有效地告诉它使用其遗留语义而不是标准的 C11 语义。 -std 的默认值在版本 4.8.5 中是 gnu90。在版本 5 中,它被更改为 gnu11,它实现了 C99/C11 inline 语义。

您正在使用默认设置,因为您的 Makefile 不包含 $(CFLAGS) 编译方法;仅在最终的 link 食谱中。 (您可以在 make 打印出的命令中看到这一点)。

虽然 -std=gnu11 应该有效,但最好改用 -std=c11。并考虑升级到更新的 GCC 版本。

您需要在编译时传递标准版本标志。在链接阶段传递它没有任何好处。

所以你的 makefile 应该是这样的:

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)

我已经用这个 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 

和 gcc 4.6.4 并且它工作正常,只要 compilations(而不是链接命令)至少得到 -std=c99(它不适用于4.6.4 的默认值)。

(旁注:header guard 不应以下划线和大写字母开头。)