gets
函数首先在C99中被弃用,最终在C11中删除。 然而,在 C 库中没有直接替代它。
fgets()
不是直接替换,因为它不会剥离最终的'n'
,而文件末尾可能没有。 许多程序员也弄错了。
有一个单行词可以删除换行:buf[strcspn(buf, "n")] = ' ';
,但它不是平凡的,通常需要解释。 它也可能效率低下。
这是适得其反的。 许多初学者仍然使用gets()
因为他们的老师很蹩脚或他们的教程过时了。
Microsoft提出了gets_s()
和许多相关函数,但它并没有静默地截断超长行,这种约束冲突的行为并不完全简单。
BSD和GNU libc都有getline
,在POSIX中标准化,通过realloc
分配或重新分配缓冲区。
教初学者了解这种混乱的最佳方式是什么?
这个问题的本质是这样的,会有猜测和意见。但是我们可以从C99的基本原理和C11标准中找到一些信息。
C99 的基本原理,当gets()
被弃用时,陈述了弃用它的以下原因:
由于 get 不检查缓冲区溢出,因此通常不安全 在其输入不受程序员控制时使用。这有 导致一些人质疑它是否应该出现在标准中 都。委员会认为,在 程序员确实有足够的特殊情况 控制输入,并且作为长期存在的做法,IT 需要一个标准规范。然而,一般来说,首选 函数是 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++等的原子模型(。 gets
没有这样的替代品可以采用,可能是因为fgets
通常被认为是优越的(当文件结束时没有换行符时,它是无损的(。如果你真的想要直接替换,像这样的东西有效:
char buf[100];
scanf("%99[^n]%*1[n]", buf);
当然,使用起来很笨拙,尤其是当缓冲区大小可变时。
IMO,任何替换都需要通过size
和char *
目的地,因此需要对代码进行重大更改,这些更改在很大程度上取决于具体情况。 一刀切被认为是不可能的,因为size
经常在代码到达gets()
时丢失/未通过。 鉴于我们有12年的警告(C99到C11(,怀疑委员会认为问题将在2011年消失。
医 管 局!
标准C委员会应该做出一个替代品,也通过了目的地的大小。 像下面这样。 (这可能存在名称冲突问题(
char *gets_replacement(char *s, size_t size);
我尝试了利用 VLA 的基于 fgets()
的替换(在 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 = ' ';
}
return NULL;
}
size_t len = strlen(buf);
if (len > 0 && buf[len - 1] == 'n') {
buf[--len] = ' ';
}
// If input would have overrun the original gets()
if (len >= size) {
// or call error handler
if (size > 0) {
*buf = ' ';
}
return NULL;
}
return memcpy(dest, buf, len + 1);
}