直接回答问题
我喜欢罗伯特的回答,但我对我提出的问题也有一些看法。
总的来说,我不相信第 1 部分“边界检查接口”。第 2 部分“动态分配函数”的草稿中的材料更好。
如果由我决定,我会沿着第 1 部分的路线移动,但我还修改了 C99 标准 C 库中返回 achar *
到字符串开头的接口(例如strcpy()
and strcat()
),而不是返回一个指向开始的指针,它们将返回一个指向新字符串末尾的空字节的指针。这将使一些常见的习惯用法(例如重复将字符串连接到另一个字符串的末尾)更有效,因为它可以避免重复使用的代码表现出的二次行为变得微不足道strcat()
。替换都将确保输出字符串的空终止,就像 TR24731 版本一样。我并不完全反对检查接口的想法,也不反对异常处理功能。这是一项棘手的业务。
微软的实现与标准规范不一样
更新 (2011-05-08)
另请参阅此问题。可悲的是,对于 TR24731 函数的有用性而言,这是致命的,Microsoft 实现和标准之间的某些函数的定义不同,使它们无用(对我来说)。我的回答引用了vsnprintf_s()
。
例如,TR 24731-1 说接口vsnprintf_s()
是:
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdarg.h>
#include <stdio.h>
int vsnprintf_s(char * restrict s, rsize_t n,
const char * restrict format, va_list arg);
不幸的是,MSDN说接口vsnprintf_s()
是:
int vsnprintf_s(
char *buffer,
size_t sizeOfBuffer,
size_t count,
const char *format,
va_list argptr
);
参数
- 缓冲区 - 输出的存储位置。
- sizeOfBuffer - 输出缓冲区的大小。
- count - 要写入的最大字符数(不包括终止的 null),或 _TRUNCATE。
- 格式 - 格式规范。
- argptr - 指向参数列表的指针。
请注意,这不仅仅是类型映射的问题:固定参数的数量是不同的,因此是不可调和的。我也不清楚(可能也是标准委员会)同时拥有“sizeOfBuffer”和“count”有什么好处;它看起来像两次相同的信息(或者,至少,代码通常会为两个参数写入相同的值)。
scanf_s()
同样,与它的亲属 也有问题。微软表示缓冲区长度参数的类型是unsigned
(明确说明'大小参数是类型unsigned
,而不是size_t
')。相比之下,在附件 K 中,size 参数的类型是rsize_t
,它是size_t
(rsize_t
的另一个名称size_t
,但RSIZE_MAX
小于SIZE_MAX
) 的受限变体。因此,再次,对于 Microsoft C 和标准 C,代码调用scanf_s()
必须以不同的方式编写。
最初,我打算使用“安全”函数作为让一些代码在 Windows 和 Unix 上干净编译的一种方式,而无需编写条件代码。由于 Microsoft 和 ISO 的功能并不总是相同,因此失败了,因此几乎是时候放弃了。
微软vsnprintf()
在 Visual Studio 2015中的变化
在 Visual Studio 2015 文档中vsnprintf()
,它指出界面已更改:
从 Visual Studio 2015 和 Windows 10 中的 UCRT 开始,vsnprintf
不再与_vsnprintf
. vsnprintf
功能符合C99标准;_vnsprintf
为向后兼容而保留。
但是,微软的界面vsnprintf_s()
并没有改变。
Microsoft 和 Annex K 之间差异的其他示例
的 C11 标准变体localtime_s()
在 ISO/IEC 9899:2011 附件 K.3.8.2.4 中定义为:
struct tm *localtime_s(const time_t * restrict timer,
struct tm * restrict result);
与localtime_s()
定义为的 MSDN 变体相比:
errno_t localtime_s(struct tm* _tm, const time_t *time);
POSIX 变体localtime_r()
定义为:
struct tm *localtime_r(const time_t *restrict timer,
struct tm *restrict result);
除了名称之外,C11 标准和 POSIX 函数是等价的。Microsoft 函数在接口上有所不同,尽管它与 C11 标准同名。
另一个不同的例子是Microsoftstrtok_s()
和 Annex K 的strtok_s()
:
char *strtok_s(char *strToken, const char *strDelimit, char **context);
与:
char *strtok_s(char * restrict s1, rsize_t * restrict s1max, const char * restrict s2, char ** restrict ptr);
请注意,Microsoft 变体有 3 个参数,而 Annex K 变体有 4 个。这意味着 Microsoft 的参数列表strtok_s()
与 POSIX 兼容strtok_r()
——因此,如果您更改函数名称(例如通过宏),对这些参数的调用实际上是可互换的——但是标准 C(附件 K)版本与两者都不同,带有额外的参数。
Mac 和 Linux 上的不同声明的qsort_r()
问题有一个答案,该答案也讨论qsort_s()
了 Microsoftqsort_s()
定义的和 TR24731-1 定义的 - 再次,接口是不同的。
ISO/IEC 9899:2011 — C11 标准
C11 标准(2010 年 12 月草案;您可以一次从 ANSI 网上商店以 30 美元的价格获得最终标准ISO/IEC 9899:2011的 PDF 副本)确实具有 TR24731-1 功能作为可选功能标准的一部分。它们在附件 K(边界检查接口)中定义,它是“规范性的”而不是“信息性的”,但它是可选的。
C11 标准中没有 TR24731-2 函数——这很可悲,因为该vasprintf()
函数及其相关函数可能真的很有用。
快速总结:
- C11 包含 TR24731-1
- C11 不含 TR24731-2
- C18 与 TR24731 中的 C11 相同。
从 C11 的继任者中删除附件 K 的提案
Deduplicator在对另一个问题的评论中指出,在ISO C 标准委员会(ISO/IEC JTC1/SC22/WG14)之前有一个提案
它包含对附件 K 函数的一些现有实现的引用——它们都没有被广泛使用(但如果您有兴趣,可以通过文档找到它们)。
该文件以建议结尾:
因此,我们建议附录 K 要么从 C 标准的下一个修订版中删除,要么弃用然后删除。
我支持该建议。
C18 标准并没有改变 Annex K 的状态。有一篇论文N2336主张对 Annex K 进行一些更改,修复其缺陷而不是完全删除它。