障碍,获取和获取

时间:2017-10-16 06:36:58

标签: c linux posix libc

我想从stdin获得一条线。据我所知,我们永远不应该像获取的手册中所说的那样使用获取:

  

永远不要使用gets()。因为不知道就不可能说出来   数据预先获取多少个字符()将读取,和   因为gets()将继续存储超过结束的字符   缓冲,使用起来非常危险。它已经习惯了   打破计算机安全。改为使用fgets()。

它表明我们可以使用fgets()代替。 fgets()的问题在于我们事先并不知道用户输入的大小,而fgets()从流中读取的字符串只有一个小于字节的字节,正如man所说:

  

fgets()从流中读取最多一个小于大小的字符   并将它们存储到s指向的缓冲区中。阅读停止   在EOF或换行符之后。如果读取换行符,则将其存储到   缓冲区。终止空字节(' \ 0')存储在最后一个字节之后   缓冲区中的字符。

还有另一种方法是使用POSIX getline(),它使用realloc来更新缓冲区大小,这样我们可以从输入流中读取任意长度的字符串,如man所说:

  

或者,在调用getline()之前,* lineptr可以包含一个   指向malloc(3)分配缓冲区* n字节大小的指针。如果   缓冲区不足以容纳线,getline()调整它的大小   使用realloc(3),根据需要更新* lineptr和* n。

最后还有另一种使用obstack的方法,因为libc手册说:

  

除了释放顺序的这一约束之外,还有障碍物   完全一般:一个障碍物可以包含任意数量的物体   任何尺寸。它们是用宏实现的,所以分配是   通常非常快,只要对象通常很小。而且   每个对象只有空间开销是启动每个对象所需的填充   对象在合适的边界上......

因此我们可以对任何大小的任何对象使用obstack,分配非常快,只需要很小的空间开销,这不是什么大问题。我编写这段代码来读取输入字符串而不知道它的长度。

#include <stdio.h>
#include <stdlib.h>
#include <obstack.h>
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
int main(){
        unsigned char c;
        struct obstack * mystack;
        mystack = (struct obstack *) malloc(sizeof(struct obstack));
        obstack_init(mystack);
        c = fgetc(stdin);
        while(c!='\r' && c!='\n'){
                obstack_1grow(mystack,c);
                c = fgetc(stdin);
        }
        printf("the size of the stack is: %d\n",obstack_object_size(mystack));
        printf("the input is: %s\n",(char *)obstack_finish(mystack));
        return 0;
}

所以我的问题是: 使用像这样的障碍物是否安全? 是否喜欢使用POSIX getline? 我在这里错过了什么吗?有什么缺点吗? 为什么我不应该使用它? 提前谢谢。

2 个答案:

答案 0 :(得分:2)

fgetsgets没有缺点。它只是强迫确认必须知道缓冲区的大小。 gets反而要求您以某种方式神奇地知道输入(可能是恶意的)用户将输入您的程序的长度。这就是为什么{C {1}}已从C编程语言中删除的原因。它现在是非标准,而gets 标准且可移植。

至于预先知道行的长度,POSIX说必须准备一个实用程序来处理适合LINE_MAX大小的缓冲区的行。因此你可以这样做:

fgets

任何产生问题的文件都不是标准文本文件。在实践中,如果你不盲目地假设缓冲区中的最后一个字符总是char line[LINE_MAX]; while (fgets(line, LINE_MAX, fp) != NULL) (它不是),那么一切都会很好。

'\n'是POSIX标准函数。 getline是GNU libc扩展,不可移植。 obstack是为了有效地从文件中读取行而构建的,getline不是,它被构建为泛型。对于obstack,字符串在内存中/最终位置不正确连续,直到您调用obstack

如果在POSIX上使用obstack_finish,请在需要最大程度移植的程序中使用getline;为fgets上构建的非POSIX平台寻找getline仿真。

答案 1 :(得分:2)

  

为什么我不应该使用它?

好吧,如果你关心可移植性,你就不应该使用getline()。如果您专门定位POSIX系统,使用getline()

对于 obstacks ,它们特定于GNU C库,这可能已经是避免它们的强有力理由(它进一步限制了可移植性)。而且,它们并不意味着用于此目的。

如果您的目标是可移植性,请使用fgets()。根据{{​​1}}编写类似于getline()的函数并不太复杂 - 这是一个例子:

fgets()

如果找到换行符,则删除换行符并返回一个新分配的缓冲区(调用者必须#include <stdio.h> #include <stdlib.h> #include <string.h> #define CHUNKSIZE 1024 char *readline(FILE *f) { size_t bufsize = CHUNKSIZE; char *buf = malloc(bufsize); if (!buf) return 0; char *pos = buf; size_t len = 0; while (fgets(pos, CHUNKSIZE, f)) { char *nl = strchr(pos, '\n'); if (nl) { // newline found, replace with string terminator *nl = '\0'; char *tmp = realloc(buf, len + strlen(pos) + 1); if (tmp) return tmp; return buf; } // no newline, increase buffer size len += strlen(pos); char *tmp = realloc(buf, len + CHUNKSIZE); if (!tmp) { free(buf); return 0; } buf = tmp; pos = buf + len; } // handle case when input ends without a newline char *tmp = realloc(buf, len + 1); if (tmp) return tmp; return buf; } int main(void) { char *input = readline(stdin); if (!input) { fputs("Error reading input!\n", stderr); return 1; } puts(input); free(input); return 0; } )。适应您的需求。当缓冲区完全填满时,可以通过增加缓冲区大小 来改善它,只需要更多的代码......