有没有办法将 fopen_s() 与 GCC 一起使用,或者至少创建一个关于它的#define?

Posted

技术标签:

【中文标题】有没有办法将 fopen_s() 与 GCC 一起使用,或者至少创建一个关于它的#define?【英文标题】:Is there a way to use fopen_s() with GCC or at least create a #define about it? 【发布时间】:2009-10-03 08:40:34 【问题描述】:

MSVC 编译器表示 fopen() 已弃用,建议使用 fopen_s()

有什么方法可以使用fopen_s() 并且仍然可以移植?

#define有什么想法吗?

【问题讨论】:

类似问题here。 【参考方案1】:

Microsoft 的 *_s 函数不可移植,我通常使用等效的 C89/C99 函数并禁用弃用警告 (#define _CRT_SECURE_NO_DEPRECATE)。

如果您坚持,您可以在没有fopen_s() 的平台上使用代理fopen() 的适配器函数(不一定是宏!),但您必须小心映射errno_t 返回码的值来自errno

errno_t fopen_s(FILE **f, const char *name, const char *mode) 
    errno_t ret = 0;
    assert(f);
    *f = fopen(name, mode);
    /* Can't be sure about 1-to-1 mapping of errno and MS' errno_t */
    if (!*f)
        ret = errno;
    return ret;

但是,我看不出fopen_s()fopen() 更安全,所以我通常会考虑便携性。

【讨论】:

有趣的是,它们现在是 C11 的一部分(尽管在可选的附件 K 中) 比我的(现在是以前的,不再需要它了)纯宏方法要好得多。您的函数方法通过返回 errno(= EINVAL,即 22,fwiw)部分重现了 fopen_s 失败时的行为。您还可以生成 无效参数异常 以更紧密地匹配 fopen_s 行为。 顺便说一句,根据this,errno“扩展为 int 类型的静态可修改左值)直到 C++11)”,以及“扩展为线程- int 类型的局部可修改左值 (C++11 起)。"所以你的返回类型应该是 int。 有趣的是,它们现在是 C11 的一部分 Microsoft's versions are not part of Annex K:“Microsoft Visual Studio 实现了 API 的早期版本。但是,实现不完整,并且两者都不符合与 C11 或最初的 TR 24731-1 一致。...由于与规范有大量偏差,Microsoft 的实现不能被认为是符合或可移植的。”【参考方案2】:

在 C/C++ 代码中,

#ifdef __unix
#define fopen_s(pFile,filename,mode) ((*(pFile))=fopen((filename),(mode)))==NULL
#endif

在 Makefile 中

CFLAGS += -D'fopen_s(pFile,filename,mode)=((*(pFile))=fopen((filename),(mode)))==NULL'

注意,成功时 fopen_s 返回 0 而 fopen 返回一个非零文件指针。因此需要在宏的末尾添加“==NULL”,例如:

if (fopen_s(&pFile,filename,"r")) perror("cannot open file");

【讨论】:

如果fopen() 失败,您错过了fopen_s() 返回errno 的部分。【参考方案3】:

如果您使用的是 C11,fopen_s 是标准库:

http://en.cppreference.com/w/c/io/fopen

gcc中需要使用--std=C11参数。

【讨论】:

这个答案是错误的。 fopen_s() 和 Annex K of the C11 standard 中的其他功能根据 K.2 Scope, paragraph 1 是可选的:“本附件指定了一系列可选扩展……”实际上,截至 2019 年,只有 Microsoft 实施了 Annex K,而 Microsoft 实施了 "cannot be considered conforming or portable"。 @AndrewHenle 即使您可能是对的,但问题是直接要求 GCC 使用 fopen_s。所以它可能不是通用的解决方案,但它适合这个问题,说是错误的限制性太强,可能不完整。作为每 C 语言,C ANSI 在某些复杂软件中可能不是 100% 可移植的,太低级了。所以又来了:权衡。 @Raffaello GCC 从未实施过附件 K,也永远不会实施。 Some reasons given as to why:“让我们投反对票吧。它有争议,可能会导致软件错误,并且不反映社区的共识。” There's also this: “我认为附件 K 是一个大错误——委员会的最新版本落入了对实际实施标准没有兴趣的“赞助商”的滑稽动作中,并且在过去的 20 多年里,除了试图破坏 C 语言之外什么都没做” 另请参阅***.com/questions/50724726/… 以及此处的许多其他问题和答案,这些问题和答案指出了附件 K 及其*_s“安全”功能的问题。简而言之,它们是不可移植的,并且任何实际使用中的唯一实现 - 微软 - 无论如何都是不合格的。【参考方案4】:

Microsoft 的许多安全功能都包含在 C11 标准的 Annex K 中,但并未得到广泛支持,因此可移植性仍然是一个问题。在某些应用程序中需要提高安全性;也许将来支持会有所改善。

我过去,我是这样干的:

  #define fopen_s(fp, fmt, mode)          *(fp)=fopen( (fmt), (mode))

该宏简单直接,足以应付快速而肮脏的事情,但它不提供 fopen_s 的异常行为,也不提供真正的 fopen_s 函数的安全性。

@Alex B 的上述函数方法部分地再现了失败时的正确行为;他返回 errno (= EINVAL)。他的方法可以通过生成无效参数异常进一步扩展,以更全面地再现 fopen_s 的行为。

【讨论】:

【参考方案5】:
#define fopen_s(fp, fmt, mode)  (\
    *(fp)=fopen( (fmt), (mode));\
    (*(fp) ) ? 0:errno;\
)

【讨论】:

感谢您提供此代码 sn-p,它可能会提供一些有限的短期帮助。一个正确的解释would greatly improve 其长期价值,通过展示为什么这是解决问题的好方法,并将使其对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。 我只是添加了 GCC 和 Clang 支持的 "(...)" 以根据@riderBill 的 2 楼答案返回值。感谢 RiderBill。【参考方案6】:

根据https://en.cppreference.com/w/c/io/fopen 可以在标准库上启用 *_s 函数:

与所有边界检查函数一样,只有在实现定义了__STDC_LIB_EXT1__ 并且用户在包含<stdio.h> 之前将__STDC_WANT_LIB_EXT1__ 定义为整数常量1 时,才能保证fopen_s 可用。

【讨论】:

正如引用的文本所说,此选项仅在实现定义 __STDC_LIB_EXT1__ 时可用。大多数实现都没有。

以上是关于有没有办法将 fopen_s() 与 GCC 一起使用,或者至少创建一个关于它的#define?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法将 QVariant 与 QVector 一起使用?

将 gcc/g++ 仪器功能与 MinGW 一起使用?

有没有办法将 GeoFire 与 Firestore 一起使用?

有没有办法将 pipenv 与 Jupyter 笔记本一起使用?

有没有办法将 PredicateBuilder 与 Azure 表存储一起使用?

有没有办法将任务并行库(TPL)与 SQLDataReader 一起使用?