由于标准C委员会没有标准化gets()的简单替换,它应该是什么?
gets
函数首先在C99中弃用,最后在C11中删除。 然而,在C库中没有直接替代它。
fgets()
不是替代品,因为它不会删除最后的'\n'
,这可能在文件末尾不存在。 许多程序员也错了。
有一个单行删除换行符: buf[strcspn(buf, "\n")] = '\0';
,但这是非常重要的,往往需要解释。 它也可能是低效的。
这会适得其反。 许多初学者仍然使用gets()
因为他们的老师很蹩脚或他们的教程已经过时了。
微软提出了gets_s()
和许多相关的函数,但是它并没有默默地截断超长行,这种约束违规的行为并不是很简单。
BSD和GNU libc都有getline
,在POSIX中标准化,通过realloc
分配或重新分配缓冲区…
教初学者关于这个烂摊子的最佳方法是什么?
这个问题的本质就是猜测和意见。 但我们可以从C99原理和C11标准中找到一些信息。
C99基本原理 ,当不推荐使用gets()
时,请说明以下原因:弃用它:
因为gets不检查缓冲区溢出,所以当它的输入不在程序员的控制之下时使用它通常是不安全的。 这导致一些人质疑它是否应该出现在标准中。 委员会认为,在程序员对输入有足够控制的情况下,在特殊情况下,获取是有用和方便的,并且作为长期存在的实践,它需要标准规范。 但是,一般来说,首选函数是fgets(参见§7.19.7.2)。
我认为gets_s()
也不能被视为替代方案。 因为gets_s()
是一个可选的接口。 C11实际上推荐了fgets()
不是gets_s()
:
§K.3.5.4.1 ,C11草案
fgets函数允许正确编写的程序安全地处理输入行太长而无法存储在结果数组中。 通常,这要求fgets的调用者注意结果数组中是否存在换行符。 考虑使用fgets(以及基于换行符的任何所需处理)而不是gets_s。
因此,我们将fgets()
作为ISO C中gets()
的唯一真正替代品fgets()
等效于gets()
除非它有缓冲空间,否则会在换行符中读取。 那么值得引入一个新的界面,这个界面比长期广泛使用的( fgets()
)界面略有改进吗? 国际海事组织,没有。
此外,许多现实世界的应用程序不仅限于ISO C。 所以有机会使用扩展和POSIX getline()
等作为替代。
如果有必要在ISO C中找到写一个解决方案,那么很容易在fgets()
周围编写一个包装器,比如my_fgets()
会删除换行符(如果存在)。
当然,向新手教授fgets()
需要解释潜在的换行问题。 但IMO,并不难理解,有意学习C的人应该能够快速掌握它。 它(找到最后一个角色并替换它,如果它的角色“X”)甚至可以被认为是初学者的一个很好的练习。
因此,鉴于上述原因,我认为ISO C中的新函数绝不是必需的,因为它是gets()
的真正替代品。
这个问题在很大程度上要求推测缺乏委员会会议记录或其他事项的引用,但作为一般原则,委员会(WG14)通常避免发明新的界面,更喜欢记录和制定严格的现有做法(如snprintf
, long long
, inttypes.h
类型等)有时采用C之外的其他标准/接口定义(例如IEEE浮点复数数学,C ++primefaces模型等)。 gets
没有这样的替代品,可能是因为fgets
通常被认为是优越的(当文件以新行结束时它是无损的)。 如果你真的想直接替换,这样的工作:
char buf[100]; scanf("%99[^\n]%*1[\n]", buf);
当然使用它很笨重,特别是当缓冲区大小可变时。
IMO,任何替换都需要传递size
以及char *
目的地,从而需要代码更改,这些更改在很大程度上取决于具体情况。 一个尺寸适合所有人不被认为是可能的,因为时间代码获得gets()
时, size
经常丢失/不通过。 鉴于我们有12年警告(C99至C11),怀疑委员会认为问题将在2011年消失。
哈!
标准C委员会应该更换一个也超过目的地大小的替代品。 如下所示。 (这可能有名称冲突问题)
char *gets_replacement(char *s, size_t size);
我尝试了一个基于fgets()
的替换,它利用了VLA(在C11中是可选的)
char *my_gets(char *dest, size_t size) { // +2 one for \n and 1 to detect overrun char buf[size + 2]; if (fgets(buf, sizeof buf, stdin) == NULL) { // improve error handling - see below comment if (size > 0) { *buf = '\0'; } return NULL; } size_t len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') { buf[--len] = '\0'; } // If input would have overrun the original gets() if (len >= size) { // or call error handler if (size > 0) { *buf = '\0'; } return NULL; } return memcpy(dest, buf, len + 1); }