scanf动态分配

10

由于某种原因,以下代码会输出 (null):

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

int main(void)
{
    char *foo; 
    scanf("%ms", &foo);
    printf("%s", foo);
    free(foo);
}
我试图动态分配一个字符串的内存,但正如我之前所说,我的程序只会输出(null)。我通过使用getche和realloc创建了一个函数来解决此问题,但似乎这几乎是毫无意义的,因为我还必须编写如果用户输入退格、制表符等将发生什么的程序...但正如我所说,这只是一个变通方法,我更想知道为什么上面的代码不起作用...
附加信息:
我正在使用Pelles C IDE v7.00,并使用C11标准进行编译。

3
那是非标准的吗?我在我的 scanf 文档中没有看到这个。 - Greg Hewgill
1
我在我的 C11 副本中没有看到关于 m 标志的任何信息(尽管它是草案)。你从哪里得到的?在我看来,这似乎是主要针对 GCC 的特定内容,在任何其他库实现中都不太可能找到。 - AnT stands with Russia
2
也许我的编译器不支持它?在C99中有a标志,我认为根据这个文档,m标志是它的新版本。 - Keith Miller
4
m标志现在已经出现在POSIX中,是2008版中新增的。关于标准的好处... - Alan Curry
3
scanf("%ms", &foo); 是正确的。 %ms 格式符会分配内存,并“返回”指向该内存的指针。 foo 的初始值未使用。 - M.M
显示剩余6条评论
5个回答

9

我在C11标准草案的第7.21.6.2节(关于fscanf的部分)中没有看到%m。建议您避免使用它,而像在C99中一样调用malloc()


3
既然无法预测使用 scanf("%s") 时需要分配多少空间,我建议完全避免使用 scanf,转而使用 fgetssscanf - jamesdlin
7
%ms 格式符是 POSIX 对于 scanf() 函数的扩展,与标准 C 版本的 scanf() 有所不同。 - Jonathan Leffler
也许可以将此与一种方法结合使用,该方法告诉您预先接收到的数据有多大(在堆栈上)。这只是一个想法。 - Vandrey

2

%m是POSIX扩展,这可能是为什么它在Pelles C中不可用的原因。


1

在scanf/sscanf的man页面(第3页,从第270-290行)中有一个例子,它陈述了以下内容:

EXAMPLE

To use the dynamic allocation conversion specifier, specify m as a length modifier (thus %ms or %m[range]). The caller must free(3) the returned string, as in the following example:

char *p;
int n;

errno = 0;
n = scanf("%m[a-z]", &p);
if (n == 1) {
   printf("read: %s\n", p);
   free(p);
} else if (errno != 0) {
   perror("scanf");
} else {
   fprintf(stderr, "No matching characters\n");
}

As shown in the above example, it is necessary to call free(3) only if the scanf() call successfully read a string.

来源:SCANF(3)- Linux程序员手册 - GNU - 2013年01月30日


0

我正在使用Linux 3.2.0 amd 64和gcc 4.7.2。使用以下代码:

char *s; 
scanf("%ms", &s);
printf("%s", s);
free(s);

功能正常,但不接受空格。

对于静态分配,您可以改用以下方法:

char s[100];
printf("Enter string (max 99 characters): ");
scanf("%100[^\n]s", &s);
printf("You entered: %s\n", s);

希望它有所帮助!

很抱歉,但你并没有帮助他,因为如果你看一下你的 scanf() 函数,你肯定会意识到它是错误的!它应该是 scanf("%ms", s),因为 s 本身就是一个指针!请注意,在 s 前面没有 &。此外,请记住,如果程序中有任何下一个 scanf,它将无法工作!!!希望这很清楚!!! - Meninx - メネンックス
6
不,用 %ms 的话,应该写成 scanf("%ms", &s)。POSIX 中的 m 扩展会为你分配字符串并将其赋值给 s,其中 s 必须是 char* 类型。 - Rudy Velthuis
scanf("%ms", &s) 是正确的,该调用会修改存储在s中的指针。然而,**scanf("%100[^\n]s", &s)是错误的。它应该是scanf("%99[^\n]", s)。在这种情况下,需要通过值传递缓冲区的第一个字节的指针,因此s是正确的。而且,在闭合括号]之后的s是错误的,它不是转换的一部分。但是,最重要的是,缓冲区必须比字段宽度长,以留出空间放置终止空字节。对于一个100字节的缓冲区使用100个字段宽度允许用户越过缓冲区**。 - cmaster - reinstate monica

0

正如Arun所说,这个解决方案是正确的,Meninx的观点是错误的:

char *s;
scanf("%ms", &s);
printf("%s", s);
free(s);

我已经阅读了'man scanf',它说:

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


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