【问题标题】:Can I disable or ignore Apple additions to C standard headers?我可以禁用或忽略 Apple 对 C 标准头文件的添加吗?
【发布时间】:2020-12-01 23:14:14
【问题描述】:

我正在开发一个 C 应用程序,我希望它具有合理的可移植性。它可以在 Linux 上使用 gcc 和 clang 以及在 Windows 上使用 MSVC 构建。访问 Mac 后,我尝试使用命令行工具进行构建。

它无法编译,因为我的代码声明了一个函数 isnumber,而 Apple 的 ctype.h 标头也声明了一个(非标准?)isnumber。我可以重命名我的函数,这样它就不会发生冲突,但是有没有办法通过禁用或忽略所有或特定的 Apple 对标准头文件的添加来避免这种情况?例如。是否有编译器选项或预处理器编译指示可以忽略它们?

我的isnumber 无法检查字符类。下面是重现该问题的代码 - 它可以使用 clang/Linux 和 MSVC/Windows 编译,但不能在 Mac 上编译(-它不是实际代码)。

#include <ctype.h>
#include <stdio.h>

char *isnumber(void);

int main(void)
{
    char *opt = "A";

    if (isupper(*opt))
        printf("THE IS NUMBER IS: %s\n", isnumber());
    else
        printf("The IS number is: %s\n", isnumber());

    return 0;
}

char *isnumber(void)
{
    return "IS-123";
}

错误:

/Users/ ... /repro/main.c:4:7: error: conflicting types for 'isnumber'
char *isnumber(void);
      ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_ctype.h:323:1: note: previous definition is here
isnumber(int _c)

更新:

正如 Acorn 的回答和它的 cmets 所描述的,“isnumber”是函数的错误名称,因为 C11 标准保留它:

7.31 未来图书馆方向
为方便起见,以下名称分组在各个标题下。 下面描述的所有外部名称都是保留的,无论程序包含什么标头。

...

7.31.2 字符处理&lt;ctype.h&gt;
"以is 或to 开头的函数名称和一个小写字母可以添加到&lt;ctype.h&gt; 标头中的声明中。

因此,我原来的问题的“正确”解决方案是重命名 my 函数。

【问题讨论】:

  • 重命名函数有什么问题?也许你的项目是 50,000 行,你需要 30 分钟来替换所有调用...
  • 你试过用-std=c11编译吗?
  • @FelixG 我刚刚用 c89、c99、c11 和 c17 尝试了 -std=,结果相同。
  • 你看头文件有没有#ifdefs关于那些函数?
  • @FelixG 我之前做过,但再看看可能会有希望......会尝试一些事情并报告。

标签: c clang c-standard-library xcode-command-line-tools


【解决方案1】:

我正在开发一个 C 应用程序,我希望它具有合理的可移植性。

编译失败是因为我的代码声明了一个函数isnumber

C 和 POSIX 都保留所有 is[a-z]* 名称(在 POSIX 的情况下仅在包含标头时),因此代码不可移植。

使其可移植的唯一方法是避免使用这样的标识符。

一种解决方案是在您提到的来自该规范的所有标识符前面加上类似于规范名称的东西,例如xx* 或 xx_*。 C 库采用了类似的方法来避免与其他库发生冲突。

非解决方案包括:

  • 避免包含ctype.h。仍然不便携,即使在实践中它在其他系统中工作的可能性更高。
  • 使用某些宏定义禁用扩展。仍然不可移植,因为其他系统可能无法识别并仍然定义isnumber。在实践中,您最终将不得不研究如何在每个系统中执行类似的操作。

【讨论】:

  • 这是一个非常有用的信息,但我可以接受我自己的答案,因为具体问题是关于禁用可能有应用程序的 Apple 插件。例如,在 Mac 上进行初级开发的人可能希望禁用 Apple 插件以避免意外依赖 Apple 特定的东西。
  • 即使 &lt;ctype.h&gt; 不包括在内,is[a-z]* 名称仍由 C 标准(C11 7.31 和 7.31.2)保留。
  • @IanAbbott 你是对的,谢谢!我也会补充的。
  • 这里是文档的链接:open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf :-)(或至少是它的草稿)
  • 关键点是禁用 Apple 扩展不会使代码具有可移植性。事实上,这通常是在移植来自其他系统的非便携式软件时的一个逃生口,即它可以在不更改代码的情况下工作。但这意味着该软件的便携性更少,而不是更多!
【解决方案2】:

是的,它们可以被禁用。但在禁用任何东西之前,我强烈建议阅读 Acorn 的答案。仅仅因为您可以禁用它们,并不意味着您应该这样做。例如,在我的案例中,尝试禁用 Apple 添加是 错误 的解决方案。但我的问题是“可以……”而不是“应该……”。

可以通过在#include ctype.h 之前添加#define _POSIX_C_SOURCE 或#define _ANSI_SOURCE 来禁用它们。这将“禁用”库中的 Apple 插件,例如:

#define _POSIX_C_SOURCE

#include <ctype.h>
#include <stdio.h>
...

如果你只想在 maxOS 上定义这个,你可以先检查系统定义的宏__MACH__,(见this question),例如:

#ifdef __MACH__
#define _POSIX_C_SOURCE
#endif

#include <ctype.h>
#include <stdio.h>
...
解释:

Apple 的 ctype.h 包括 _ctype.h,其中额外的声明和定义由以下人员保护:

#if !defined(_ANSI_SOURCE) && (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE))

感谢 FelixG 最先为我指明了这个方向。

【讨论】:

  • _ANSI_SOURCE 来自 BSD。依赖它会使您的代码更不便携,因为您几乎可以肯定无论如何都在使用 POSIX。在这种情况下,isnumber()仍然与 POSIX 冲突。
  • 谢谢@AndrewHenle,不,我不认为我会使用 POSIX,所以 _ANSI_SOURCE 对我来说可能是正确的,但我会更新答案以反映 _POSIX_C_SOURCE 通常可能是更好的选择.
  • 我不认为我会使用 POSIX 所以你只是通过诸如fopen()/fread() 之类的函数来访问文件?您没有对目录做任何事情吗?您没有开始并等待子进程吗?你不使用套接字?您没有使用带有#if ... 负载的简单signal() 函数之外的信号功能来处理其在不同平台上的差异?你没有使用open()/read()/write()/close()?你没有检查文件的权限?
  • @AndrewHenle。正确的。如果我不得不与getc()、putc() 到stdin 和stdout 相处,我可能会失去fopen()。
猜你喜欢
  • 1970-01-01
  • 2021-04-28
  • 2011-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-23
  • 2021-01-07
相关资源
最近更新 更多