【问题标题】:Guarding function declaration not compiling保护函数声明未编译
【发布时间】:2016-08-19 14:29:41
【问题描述】:

我在我的头文件中使用#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

【问题讨论】:

  • 你必须发布test.h的内容。还有一种新的保护方法,包括 #pragma once
  • 你想用#ifdef替换#if
  • 我宁愿看到真正的输出。
  • 请注意,标头不提供test_spi_*() 函数的原型;它只提供声明。这些声明说“函数存在;函数返回int;没有关于参数的数量或类型的信息——除了它不使用省略号(...)作为可变参数”。要在 C 中为采用零参数的函数制作原型,您必须编写 int test_spi_config(void);void。 C++ 中的规则不同,但这是一道 C 题。
  • HAS_SPI 是如何定义的? #define HAS_SPI 1? -DHAS_SPI=1? -DHAS_SPI?没有定义?值是0 而不是1?我的猜测是你没有定义HAS_SPI,所以预处理器把它当作零,而#if 0 没有包含来自头文件的声明,也没有在源文件中定义代码。

标签: c gcc makefile


【解决方案1】:

预处理器功能

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

我想你期待的是,如果标题有:

#if HAS_SPI
extern int test_spi_config(void);
#endif

并且实现文件有:

int test_spi_config(void)
{
    …
    return 0;
}

那么编译器本身不会显示test_spi_config() 的代码,因为标头没有声明它。

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

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

#if HAS_SPI
int test_spi_config(void)
{
    …
    return 0;
}
#endif

因此,在您的问题中,标题中条件定义的原型不会影响函数周围的文本;你需要明确的测试。

您还应该将每个对test_spi_config()test_spi_transfer() 的调用括在适当的#if HAS_SPI / #endif 条件中。这可能会对周围的逻辑产生影响,因此包含的块可能比单个函数调用大 - 特别是如果您需要测试或保存来自 test_spi_config() 的返回值(并且,如果您不这样做,为什么函数首先返回一个值?)。

符号

#if HAS_SPI

请注意,这些条件通常写为#ifdef HAS_SPI#if defined(HAS_SPI),因此HAS_SPI 的定义无关紧要。对于#if 条件,如果您在某处(或命令行等效项)有#define HAS_SPI 0,则代码将被省略,而对于defined 变体,它将被包含在内。哪个正确取决于您的需求——但使用#ifdef 比使用#if 更常见。

保持一致(始终使用#if 或始终使用#ifdef)比使用哪个更重要。

【讨论】:

    【解决方案2】:

    是的,这是绝对正常的。

    C 编译器参见test_spi_* 函数的声明,并为每个包含 logic.h 的 .c 文件生成存根代码。但是在链接阶段找不到这些函数的定义,因为它们被预处理器省略了。因此,链接错误。

    【讨论】:

    • 我怀疑这里的情况。 #if 条件在 .h 和 .c 中相同。
    • 所以这是正常的,你需要在c文件和头文件中加入警卫对吗?
    • @homeGrown 如果你想节省空间 - 是的。
    • “为每个.c 文件发出存根代码”是什么意思?对于 C 编译器所做的任何事情,我认为这不是正常的术语。
    • 目标文件仅包含对其编译的代码使用的函数(或变量)的引用。如果预处理器排除了对test_spi_config() 的引用,则目标文件将不包含对test_spi_config() 的引用。真的,当你考虑它时,这也是一样的。如果不是这样,源文件包含的每个系统头文件中提到的每个函数都将从目标文件中引用,这将导致完全不必要的庞大程序——尤其是在使用静态库而不是共享库的情况下。
    猜你喜欢
    • 2021-10-01
    • 2014-09-28
    • 2014-08-09
    • 1970-01-01
    • 1970-01-01
    • 2016-08-11
    • 2011-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多