未定义的gets_s参考?

时间:2013-03-01 01:57:19

标签: c c11 tr24731

我在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&amp; 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;
}

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

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

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

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

warning: implicit declaration of function ‘gets_s’

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

int foo(...);

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

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

正如@Keith Thompson所提到的,以及C11附件K“Bounds-checking interfaces”的其余部分,可选,即使是完全符合C11的实现(gcc还没有)。实现可以定义宏__STDC_LIB_EXT1__并提供接口 - 或者不是。

所以,基本上,使用fgets