使用scanf从控制台读取无界线

时间:2015-04-16 21:02:41

标签: c memory-management

我需要阅读有限但无限长的字符串。 我们只学习了scanf所以我想我不能使用fgets。 无论如何,我已经在长度大于5的输入上运行了这段代码。

char arr[5];
scanf("%s", arr);

char *s = arr;
while (*s != '\0')
    printf("%c", *s++);

scanf继续扫描和写入溢出的部分,但它似乎是一个黑客。这是一个好习惯吗?如果没有,我该怎么读呢?

注意:我们了解了alloc函数系列。

6 个答案:

答案 0 :(得分:1)

scanf是这项工作的错误工具(就像大多数工作一样)。如果您需要使用此功能,请一次使用char阅读一个scanf("%c", &c)

您编码误用scanf():您正在传递arr,指向char的指针数组的地址,而不是char的数组。

您应该使用char分配一个malloc数组,在其中读取字符,并在它太小时使用realloc进行扩展,直到获得'\n'EOF

如果您可以回放stdin,您可以先计算要使用scanf("%*s%n", &n);读取的字符数,然后将目标数组分配到n+1个字节,rewind(stdin);并重新设置 - 使用scanf("%s", buf);将字符串读入缓冲区。 这是一项有风险的业务,因为某些流如控制台输入无法重新启动。

例如:

fpos_t pos;
int n = 0;
char *buf;

fgetpos(stdin, &pos);
scanf("%*[^\n]%n", &n);
fsetpos(stdin, &pos);
buf = calloc(n+1, 1);
scanf("%[^\n]", buf);

由于你应该只知道一些基本的C,我怀疑这个解决方案是你所期望的,但我想不出用标准C一步读取无界字符串的任何其他方法。 / p>

如果您正在使用glibc并且可以使用扩展程序,则可以执行以下操作:

scanf("%a[^\n]", &buf);

PS:故意忽略所有错误检查和处理,但应在实际分配中处理。

答案 1 :(得分:1)

%as%ms(POSIX)可以用于此目的如果你使用gcc和glibc。(不是C标准)

#include <stdio.h>
#include <stdlib.h>

int main(void){
    char *s;
    scanf("%as", &s);
    printf("%s\n", s);
    free(s);
    return 0;
}

答案 2 :(得分:1)

缓冲区溢出是一个瘟疫,是最着名但最难以捉摸的错误。所以你绝对不应该依赖

由于您已经了解了malloc()和朋友,我想您应该使用它们。

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

// Array growing step size
#define CHUNK_SIZE  8

int main(void) {
    size_t arrSize = CHUNK_SIZE;
    char *arr = malloc(arrSize);
    if(!arr) {
            fprintf(stderr, "Initial allocation failed.\n");
            goto failure;
        }

    // One past the end of the array
    // (next insertion position)
    size_t arrEnd = 0u;

    for(char c = '\0'; c != '\n';) {
        if(scanf("%c", &c) != 1) {
            fprintf(stderr, "Reading character %zu failed.\n", arrEnd);
            goto failure;
        }

        // No more room, grow the array
        // (-1) takes into account the
        // nul terminator.
        if(arrEnd == arrSize - 1) {
            arrSize += CHUNK_SIZE;
            char *newArr = realloc(arr, arrSize);
            if(!newArr) {
                fprintf(stderr, "Reallocation failed.\n");
                goto failure;
            }
            arr = newArr;

            // Debug output
            arr[arrEnd] = '\0';
            printf("> %s\n", arr);
            // Debug output
        }

        // Append the character and
        // advance the end index
        arr[arrEnd++] = c;
    }
    // Nul-terminate the array
    arr[arrEnd++] = '\0';

    // Done !
    printf("%s", arr);

    free(arr);
    return 0;

failure:
    free(arr);
    return 1;
}

答案 3 :(得分:0)

尝试限制接受的字符数量:

scanf("%4s", arr);

答案 4 :(得分:0)

只是你在arr[5]以外写作。 &#34;希望&#34;你是否继续写下过程的已分配内存,但是如果你超越了你,最终会得到segmentation fault

答案 5 :(得分:0)

考虑

1)malloc()在许多系统上只分配内存,而不是使用它。在分配内存之前,不会发生下划线物理内存使用情况。见Why is malloc not "using up" the memory on my computer?

2)无限制的用户输入是不现实的。鉴于应该使用一些上限来防止黑客和恶意用户,简单地使用大缓冲区。

如果您的系统可以使用这两个想法:

char *buf = malloc(1000000);
if (buf == NULL) return NULL; // Out_of_memory
if (scanf("%999999s", buf) != 1) { free(buf); return NULL; } //EOF

// Now right-size buffer
size_t size = strlen(buf) + 1;
char *tmp = realloc(buf, size);
if (tmp == NULL) { free(buf);  return NULL; } // Out_of_memory
return tmp;

修正了每@chqrlie条评论。