1

我使用以下命令在 Ubuntu 4.6.1 和 SUSE 4.6.2 上使用 gcc

gcc gets_s.c

我的源代码是

// Read and Display Lines
// gets_s.c

#include <stdio.h>

int main(void)
{

    char first_name[11]; 
    char last_name[11]; 

    printf("First Name : ");
    gets_s(first_name, 11);
    printf("Last Name  : ");
    gets_s(last_name, 11);
    puts(first_name);
    puts(last_name);

    return 0;

}

详细说明我的问题:

对我来说,主要问题是行输入和保存的行之间的一一对应关系。

成功时,fgets 和 gets_s 之间的区别在于 fgets 包含换行符终止符,而 gets_s 将换行符终止符替换为空终止符,以便在行输入和成功调用 gets_s 之间保持一一对应。

对于溢出缓冲区长度的输入,fgets 接受适合缓冲区的字符数,并将其余字符留在输入缓冲区中以供下一个 fgets 使用。

标准(K.3.5.4.1)规定,gets_s(与gets不同)需要在n-1个字符内出现换行符、EOF或读取错误。因此溢出是违反运行时约束的。如果存在运行时约束违规,则将缓冲区中的第一个字符设置为空字符,并读取并丢弃 stdin 输入缓冲区中的字符,直到读取换行符为止,发生文件结尾或发生读取错误。

因此,在成功时,我期望:

>fgets

First Name : Chris
Last Name  : Szalwinski
Chris

Szalwinski

>

>gets_s
First Name : Chris
Last Name  : Szalwinski
Chris
Szalwinski
>

溢出时,我预计 fgets 和 gets_s 的行为会有所不同。换句话说,

>fgets
First Name : Christopher
Last Name  : Christophe
r


>

>gets_s
First Name : Christopher
Last Name  : Szalwinski

Szalwinski

>

请注意我希望 gets_s 如何完全删除第一行输入的内容。

如果主要问题是输入行和保存行之间的一一对应关系,这在调试中很重要,我们仍然需要编写自己的函数(类似于 K&R 的 getline)

char *gets_s(char *s, int n) 
{
    int i, c;
    for (i = 0; i < n - 1 && (c = getchar()) != EOF && c != (int)'\n'; i++)
        s[i] = c;
    s[i] = '\0';
    while (n > 1 && c != EOF && c != (int)'\n')
        c = getchar();
    return c != EOF ? s : NULL;
}

使用这样的函数,保持一一对应,缓冲区饱和并且没有运行时约​​束违规。

我得出这个结论是否正确。

4

2 回答 2

3

您是否尝试过传递 `-std=c11'?

根据this pagegets_s在C11中进行了介绍。假设您使用的是 GCC,您可以使用 `-std=c11' 选项启用有限的 C11 支持。

于 2013-03-01T02:16:39.727 回答
1

您应该做的第一件事是启用编译器警告。例如,传递-Wall给 GCC。完成后,它将发出以下信息:

warning: implicit declaration of function ‘gets_s’

这基本上意味着编译器不知道gets_s()函数是什么。那么它到底是如何编译的呢?在 C 中,这称为隐式函数声明,它基本上表示如果没有函数声明,编译器应该假定存在一个返回整数并接受任意数量参数的函数。例如:

int foo(...);

所以编译器很乐意为您生成代码。在您的特定情况下,从那里下山。你看,如果你使用了一个实际存在的函数(比如,printf()标准库中确实存在的标准之类的东西),那么一切都会很好(几乎,还有更多的陷阱)。实际上,虽然gets_s()在 C11 之前的世界中没有这样的东西(如果我没记错的话,它只出现在 Microsoft 的 C 库中)。因此,链接器不能简单地找到它。这就是为什么它放弃尝试将程序组装在一起并吐出您收到的错误消息的原因。换句话说——不要使用gets_s. 改为使用fgets。它几乎是一样的——唯一的区别是它是标准的,它希望你指定FILE *(你给它stdin)。man fgets您可以通过在终端中键入来阅读这些文档。或者(因为您的系统上可能未安装手册页),您可以在此处在线找到它。

正如这里很少有朋友指出的那样,gets_s()是在C11中添加的。不幸的是,C11 没有在 Ubuntu 使用的 GCC(和 glibc)中完全实现。这项工作正在进行中,您可以在 GCC 的C11 Status Wiki中查看其状态。

正如@Keith Thompson 提到的,与C11 Annex K“边界检查接口”的其余部分一起,是可选的,即使对于完全符合C11 的实现(gcc 还没有)也是如此。一个实现可以定义宏__STDC_LIB_EXT1__并提供接口——也可以不提供。

所以,基本上,使用fgets.

于 2013-03-01T02:23:47.620 回答