for循环内的scanf()函数不能正常工作

3
我尝试从标准输入中读取多个字符串并存储到一个临时变量中。
每个字符串都会在换行符后输入。
这是我的尝试:
#include <stdio.h>
int main() {
    for (int i=0; i<10; i++) {
        char string;
        scanf("%s\n", &string);
        printf("%d\n", i);
    }
}

for循环未按预期运行,只循环了两次,似乎scanf正在干扰循环。

标准输入:

test
test

标准输出:

7631717

有没有一种方法可以动态地将标准输入的每行内容存储到 char 变量中,而不需要固定大小或长度?


始终检查成功扫描的项目数。也就是说,你必须检查scanf的返回值。始终如此。如果scanf调用不在赋值的右侧,则这是一个错误。 - William Pursell
除非在像 while ( scanf(...) == ... 这样的情况下,否则我所说关于在赋值语句右侧的说法是夸张的。但是你必须检查返回值! - William Pursell
3个回答

3

如果你想读取一个字符串时不受给定大小的限制,可以使用修改符 'm':

char * p;

if (scanf("%ms", &p) == 1) {
  ...use p;
  free(p); /* when stop to be usefull
}

来自于 scanf 函数的说明:

可选的 'm' 字符。它用于字符串转换(%s,%c,%[)中,并减轻了调用者分配相应缓冲区以容纳输入的需要:相反,scanf()分配足够大小的缓冲区,并将该缓冲区的地址分配给相应的指针参数,该参数应为 char * 变量的指针(在调用之前不需要初始化此变量)。调用者在不再需要时应随后释放此缓冲区。

之前的方式允许读取一个“单词”,并在开头删除空格(' ',换行符,制表符等)以读取一整行:

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

int main()
{
  char * p;

  if (scanf(" %m[^\n]", &p) == 1) {
    printf("'%s'\n", p);
    free(p);
  }
  else
    puts("error (EOF...)");
  
  return 0;
}

编译和执行:

/tmp % gcc -Wall c.c
/tmp % ./a.out
    aze qsd
'aze qsd'
/tmp % ./a.out < /dev/null
error (EOF...)
/tmp % 

如果您没有 (not posix ...) 修改器来按原样读取整行,则可以执行以下操作:

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

int main()
{
  char * p = malloc(1);
  size_t len = 0;
  int c;
  
  while (((c = getchar()) != '\n') && (c != EOF)) {
    p = realloc(p, len+1);
    if (p == NULL) {
      puts("not enough memory");
      return -1;
    }
    p[len++] = c;
  }
  p[len] = 0;
  
  printf("'%s'\n", p);
  free(p);
  
  return 0;
}

编译和执行:

/tmp % gcc -Wall c.c
/tmp % ./a.out
  aze qsd
'  aze qsd'
/tmp % ./a.out < /dev/null
''
/tmp % 

1
+1 我之前不知道 scanf() 中的 m 修饰符,这是在各种情况下使用它的好例子。 - Rohan Bari
1
+1 对于 m;不知道它(并且仍然无法在 C 标准中找到它(例如此在线草案 http://port70.net/~nsz/c/c11/n1570.html#Contents)? - Stephan Lechner
5
m 不是 C 标准的一部分,但在 POSIX 标准中可用。因此,如果你使用的系统不支持 POSIX,就必须寻找其他替代方案。 - P.P
1
@RohanBari 我编辑了我的答案,添加了另一个例子。 - bruno
1
@StephanLechner 是的,这可能就是为什么它不经常被使用,也是我第一次在 S.O. 的回答中提到它的原因(如果我没记错的话)^^ - bruno
显示剩余3条评论

2

char string;并不是一个字符串,而是一个单个字符。使用格式%s读取字符串到一个单个字符中将是未定义的行为,因为一旦您输入至少一个字符,就会附加字符串终止字符。

编写...

char string[100];
scanf("%99s", string);

一切都应该很好。

顺便提一下:如果你想读取完整的行(包括开头、中间和结尾的空格),考虑使用 fgets 而非 scanf


有没有一种方法可以动态获取输入,而不需要固定大小/长度? - dkkl_codex

1
有没有一种方法可以动态地将stdin的输入/每行存储到一个字符变量中,而不需要固定大小/长度?
如果想要使用POSIX路线,请使用getline()来“动态存储stdin的输入/每行”。
如有需要,可以去掉潜在的尾随换行符。
       char *line = NULL;
       size_t len = 0;
       ssize_t nread;

       while ((nread = getline(&line, &len, stdin)) != -1) {
           if (nread > 0 && line[nread-1] == '\n') line[--nread] = 0;
           printf("Retrieved line of length %zu: <%s>\n", nread, line);
       }

       free(line);

我怀疑 nread > 0 && line[nread-1] == '\n' 可以简化为 line[nread-1] == '\n',因为我没有看到 nread 返回0的情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接