您是否在追踪TR24731-2的进展?
在收集问题数据时,我并没有追踪它,直到我访问了标准网站。 asprintf()
和vasprintf()
函数可能很有价值;我会使用它们。对于内存流I/O函数,我不确定。C级别将strdup()
标准化,这将是一个巨大的进步。对我来说,这似乎比第一部分(边界检查)接口更少争议。
总的来说,我不太相信第一部分“边界检查接口”。第二部分“动态分配函数”的草案材料更好。如果由我决定,我会沿着第一部分的方向前进,但也要修订在C99标准C库中返回字符串开头的char *
的接口(例如strcpy()
和strcat()
),以便它们返回指向新字符串结尾的空字节的指针。这将使一些常见的惯用语(例如重复将字符串连接到另一个字符串末尾)更加高效,因为它将使避免重复使用strcat()
的代码所展示的二次行为变得微不足道。所有替换都将确保输出字符串以null结尾,就像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
);
参数
- buffer - 输出缓冲区的存储位置。
- sizeOfBuffer - 输出缓冲区的大小。
- count - 写入字符的最大数目(不包括终止空字符)或 _TRUNCATE。
- format - 格式化说明符。
- argptr - 指向参数列表的指针。
需要注意的是,这不仅仅是类型映射的问题:固定参数的数量是不同的,因此无法调和。另外,对于具有 'sizeOfBuffer' 和 'count' 的功能,我也不清楚(也许标准委员会也不清楚)有何好处;看起来像是相同的信息重复了两遍(或者至少通常情况下,两个参数将使用相同的值编写代码)。
scanf_s()
及其相关函数也存在问题。 Microsoft 指出缓冲区长度参数的类型为 unsigned(明确表示“大小参数的类型为 unsigned,而不是 size_t”)。相比之下,在附录 K 中,大小参数的类型为 rsize_t
,这是 size_t
的限制变体(rsize_t
是 size_t
的另一个名称,但 RSIZE_MAX
比 SIZE_MAX
小)。因此,调用 scanf_s()
的代码必须在 Microsoft C 和标准 C 中编写不同的代码。
最初,我计划使用“安全”函数作为一种在 Windows 和 Unix 上使代码能够编译而无需编写条件代码的方式。由于 Microsoft 和 ISO 函数并不总是相同,因此现在放弃这种计划已经是时候了。
Visual Studio 2015 中 Microsoft vsnprintf()
的更改
在 Visual Studio 2015 的 vsnprintf() 文档中,注意到接口发生了变化:
从 UCRT 开始(Visual Studio 2015 和 Windows 10),vsnprintf
不再与 _vsnprintf
相同。 vsnprintf
函数符合 C99 标准;_vnsprintf
保留了向后兼容性。
然而,Microsoft vsnprintf_s()
的接口未更改。
Microsoft 和 Annex K 之间的其他差异示例
localtime_s()
的 C11 标准变体在 ISO/IEC 9899:2011 附录 K.3.8.2.4 中定义为:
struct tm *localtime_s(const time_t * restrict timer,
struct tm * restrict result);
相对于MSDN版本的localtime_s()
,定义如下:
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函数除了名称之外是等效的。尽管与C11标准共享名称,但Microsoft函数在接口上有所不同。
另一个差异的例子是Microsoft的strtok_s()
和Annex K的strtok_s()
:
char *strtok_s(char *strToken, const char *strDelimit, char **context);
vs:
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(Annex K)版本与两者都不同,并多了一个额外的参数。
在Mac和Linux上声明qsort_r()
不同的问题有一个答案,还讨论了由Microsoft定义的qsort_s()
和TR24731-1定义的qsort_s()
——接口也是不同的。
ISO/IEC 9899:2011 - C11标准
C11标准(2010年12月草案;曾经可以从ANSI网上商店以30美元购买ISO/IEC 9899:2011的最终标准PDF副本)将TR24731-1函数作为可选部分包含在其内。它们定义在Annex K(边界检查接口)中,该附件是规范性的而不是信息性的,但是它是可选的。
C11标准没有TR24731-2功能 - 这很遗憾,因为vasprintf()
函数及其相关函数可能非常有用。
快速摘要:
- C11包含TR24731-1
- C11不包括TR24731-2
- C18与C11在TR24731方面相同。
建议从C11的后继版本中删除Annex K
Deduplicator在另一个问题的评论中指出,ISO C标准委员会(ISO/IEC JTC1/SC22/WG14)正在考虑删除Annex K。
文档中提到了一些Annex K函数的现有实现,但它们并不广泛使用(如果您感兴趣,可以通过文档找到它们)。
文档最后建议:
因此,我们建议将Annex K从下一版C标准中删除,或者弃用然后删除。
我支持这个建议。
C18标准没有改变Annex K的状态。有一篇论文N2336主张对Annex K进行一些修改,修复其缺陷而不是完全删除它。
strlen()
添加到代码中”的含义。有时候strlen()
显然不是正确的答案,比如当将缓冲区传递给I/O函数(例如gets_s()
)时。但也许你可以详细说明一下你的想法? - Jonathan Lefflerrealloc()
,因为需要保护的函数不会分配内存。例如,strcpy()
函数不会进行内存分配;即使有垃圾回收,你也不能合理地修改它以执行内存分配,因为人们通常不使用返回值,而是使用作为第一个参数传递给strcpy()
的值进行进一步操作。gets()
和strcat()
也存在类似的问题。至少它们返回一个可能指向重新分配空间的char *
(并不保证参数已经分配)。_ […继续…]_ - Jonathan Lefflersprintf()
这样不返回char *
的函数更糟糕;它们没有办法告诉调用代码它们“重新分配”了结果所在的内存。请注意,TR 24731-2未能成为C11的原因之一是它们将是显示进行内存分配的第一个函数,除了malloc()
等函数外。请花时间研究这些函数的功能、Annex K / TR 24731-1 函数的功能、它们这样做的原因等等。做出这些决策有一些合理的理由。 - Jonathan Leffler