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 不应以下划线和大写字母开头。)
我对如何在 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 不应以下划线和大写字母开头。)