我对如何在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时,编译器是否仍会内联我的函数?
答案 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的默认设置)
(附带说明:标题保护符不应以下划线和大写字母开头。)