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

时间:2018-10-29 18:35:56

标签: c gcc inline

我对如何在c中内联函数(从C99开始)感到困惑。在“ C编程,一种现代方法”(KN King,第二版)的第18.6节中,或在this教程中的第3部分(在“使用内联函数的策略”下)中,给出了内联函数的定义在头(.h)文件中,然后该函数在源(.c)文件中再次被列为extern。

例如,我在做什么:在标题“ 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”中定义该函数并在头文件中对其进行声明时,没有得到以上错误。我正在使用的gcc版本是 gcc(GCC)4.8.5 20150623(Red Hat 4.8.5-28)。

我的问题是:

(1)那么,为什么在网上找到的“ C编程,一种现代方法”和有关内联函数的教程建议在头文件中定义该函数,并再次将其作为源文件中的extern列出?这样做对我来说是编译器错误。

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

2 个答案:

答案 0 :(得分:2)

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

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

您使用的是默认设置,因为您的Makefile在编译配方中不包含$(CFLAGS);仅在最终链接配方中。 (您可以在make打印的命令中看到它。)

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

答案 1 :(得分:2)

编译时需要传递standard-version标志。在链接阶段通过它时,效果不好。

所以您的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的默认设置)

(附带说明:标题保护符不应以下划线和大写字母开头。)