由于标准 C 委员会没有标准化 gets() 的简单替代品,它应该是什么



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(通常避免发明新的接口,而更喜欢记录和制定严格的现有实践(如snprintflong longinttypes.h类型等(,有时采用C以外的其他标准/接口定义(例如IEEE浮点数的复杂数学, 来自C++等的原子模型(。 gets没有这样的替代品可以采用,可能是因为fgets通常被认为是优越的(当文件结束时没有换行符时,它是无损的(。如果你真的想要直接替换,像这样的东西有效:

char buf[100];
scanf("%99[^n]%*1[n]", buf);

当然,使用起来很笨拙,尤其是当缓冲区大小可变时。

IMO,任何替换都需要通过sizechar *目的地,因此需要对代码进行重大更改,这些更改在很大程度上取决于具体情况。 一刀切被认为是不可能的,因为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);
}

最新更新