有没有办法将 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 一起使用?
有没有办法将 GeoFire 与 Firestore 一起使用?
有没有办法将 pipenv 与 Jupyter 笔记本一起使用?