C 保护未编译的函数声明

C 保护未编译的函数声明,c,gcc,makefile,C,Gcc,Makefile,我用#define保护头文件中的函数,我通过命令行使用make将其添加为全局编译标志。这是为了在我不使用某些代码时减少总体二进制大小 我想我可以在头文件中保护它们,而c文件中的函数会自动编译出来。这不是为我编写的。Make正在报告函数未声明。我必须在C文件中的函数周围放置防护装置,使其能够正常编译 这是预期的行为还是函数声明就足够了 头文件 #ifndef __LOGIC_H__ #define __LOGIC_H__ #include "test.h" #if (HAS_SPI) int

我用#define保护头文件中的函数,我通过命令行使用make将其添加为全局编译标志。这是为了在我不使用某些代码时减少总体二进制大小

我想我可以在头文件中保护它们,而c文件中的函数会自动编译出来。这不是为我编写的。Make正在报告函数未声明。我必须在C文件中的函数周围放置防护装置,使其能够正常编译

这是预期的行为还是函数声明就足够了

头文件

#ifndef __LOGIC_H__
#define __LOGIC_H__

#include "test.h"

#if (HAS_SPI)
int test_spi_config(void);
int test_spi_transfer(void);
#endif
#endif /* __LOGIC_H__ */
C文件

#include "logic.h"

#if (HAS_SPI) /* Wont compile without this */
int test_spi_config(void) {
     /* code */
}
#if (HAS_SPI) /* Wont compile without this */
int test_spi_transfer(void) {
     /* code */
}
#endif
#endif

是的,这是绝对正常的

C编译器查看
test\u spi.*
函数的声明,并为包含logic.h的每个.C文件发出存根代码。但是在链接阶段,找不到这些函数的定义,因为它们被预处理器省略了。因此,链接错误。

预处理器功能 预处理器执行简单的文本转换。它根本不太懂C。特别是,它不了解函数的声明或定义;它不知道或不关心它正在解析的文本是否是函数的一部分,尤其是它不知道它正在处理哪个函数

我认为您希望,如果标题有:

#if HAS_SPI
extern int test_spi_config(void);
#endif
实施文件包括:

int test_spi_config(void)
{
    …
    return 0;
}
那么编译器本身将不会显示
test\u spi\u config()
的代码,因为头没有声明它

这是一个不准确的预期。预处理器在函数定义周围看不到以
开头的一行,并且在本讨论中没有宏会更改源代码的文本(例如,没有
\define test\u spi\u config()rand()
),因此函数定义只需复制到主编译器

函数定义本身必须受到与原型声明等效(最好相同)条件的保护:

#if HAS_SPI
int test_spi_config(void)
{
    …
    return 0;
}
#endif
因此,在您的问题中,标题中有条件定义的原型不会影响函数周围的文本;你需要明确的测试

您还应该将对
test\u spi\u config()
test\u spi\u transfer()
的每个调用都包含在适当的
#if HAS(如果有_spi
/
#endif
条件中。这可能会对周围的逻辑产生影响,因此所包含的块可能比单个函数调用大-尤其是当您需要测试或保存来自
test\u spi\u config()
的返回值时(如果不需要,为什么函数首先要返回值?)

符号 请注意,这些条件通常写为
#ifdef HAS#u SPI
#if defined(HAS#SPI)
,因此定义
HAS#u SPI
并不重要。使用
#if
条件,如果您有
#define在某个地方(或等效命令行),那么代码将被省略,而使用
定义的
变体,它将被包括在内。哪一种是正确的取决于您的需要-但使用
#ifdef
比使用
#if
更常见


更重要的是保持一致性-始终使用
#if
或始终使用
#ifdef
-而不是使用哪个。

您必须发布
test.h
的内容。另外,还有一种新的方法可以使用
#pragma once
保护includes。您想用
#ifdef
替换
#if
,我更愿意看到实际的输出。请注意,标题没有为
测试spi*()
函数提供原型;它只提供声明。这些声明表示“函数存在;函数返回一个
int
;没有关于参数数量或类型的信息-除了它不使用省略号(
)作为可变参数”。要在C中为一个采用零参数的函数创建原型,必须编写
inttest\u spi\u config(void)void
进行编码>。C++中的规则是不同的,但这是一个C问题。<代码> HasySPI<代码>定义了什么?代码>#定义是否有SPI 1
<代码>-DHAS_SPI=1<代码>-DHAS_SPI?它没有定义?值是
0
,而不是
1
?我的猜测是您没有定义
具有_SPI
,因此预处理器将其视为零,如果0不包含头中的声明,或者在源文件中定义代码,那么
\#如果.h和.c中的条件相同,那么这是正常的,您将需要在c文件以及头文件中设置防护措施,对吗?@homebrown如果您想节省空间-是的。您所说的“为每个
.c
文件发出存根代码”是什么意思?对于C编译器所做的任何事情,我都不认为这是正常的术语。对象文件只包含对它编译的代码所使用的函数(或变量)的引用。如果预处理器排除了对
test\u spi\u config()
的引用,则对象文件将不包含对
test\u spi\u config()
的引用。当你想起来的时候,这也一样,真的。如果不是这样,源文件包含的每个系统头中提到的每个函数都将从目标文件中引用,这将导致完全不必要的庞大程序,特别是在使用静态库而不是共享库的情况下。
#if HAS_SPI