这条线做什么?

时间:2012-12-16 17:59:23

标签: c

以下代码不是由我制作的。我正在搜索并在其他人的问题中找到它。

#include <stdio.h>
#define NAME_MAX    80
#define NAME_MAX_S "80"

int main(void)
{
    static char name[NAME_MAX + 1]; // + 1 because of null
    if(scanf("%" NAME_MAX_S "[^\n]", name) != 1) // This line
    {
        fputs("io error or premature end of line\n", stderr);
        return 1;
    }

    printf("Hello %s. Nice to meet you.\n", name);
}

你能告诉我标记的行是什么吗?

5 个答案:

答案 0 :(得分:7)

这是一个字符串连接。当你编写字符串时,你可以使它们在编译时连接,所以:

 "%" NAME_MAX_S "[^\n]",

最终将成为:

 "%80[^\n]"
然后

scanf会读取名为name的变量80个不是newline的字符。

答案 1 :(得分:3)

在预处理器替换之后,它看起来像这样:

if(scanf("%" "80"_MAX_S "[^\n]", name) != 1) // <-- This line

相当于:

if(scanf("%80[^\n]", name) != 1) // <-- This line

最多可读取80个字符或换行符。

name的大小为81.因此它可以容纳80个字符+ nul终止符。这通常是为了在读取输入时避免缓冲区溢出。

答案 2 :(得分:3)

同时使用两种有些模糊的语言功能:

  1. 字符串粘贴。预处理后,该行

    if(scanf("%" "80" "[^\n]", name) != 1)
    

    然后将相邻的字符串文字粘贴在一起,因此对于编译器的后续部分,就好像它说... scanf("%80[^\n]", name) ...

  2. 不太常见的scanf转化。 "%[...]""%s"没有完全不同,因此这与"%80s"转换非常相似。我确信您可以在[手册页或其他参考文件中查找scanf(3)作为scanf转换说明符。

答案 3 :(得分:2)

它将stdin中的不超过80个(非换行)字符读入name变量。

如果遇到换行符,则会停止扫描。

它看起来很奇怪,因为它使用了一个稍微模糊且没有很好的C语言特征:它能够将相邻的常量字符串折叠成一个。

(它实际上是一个非常方便的功能:它允许在很多行上包装长字符串,并允许宏用常量字符串做有用的事情)

......所以,从根本上想象这一行看起来像:

if(scanf("%80[^\n]", name) != 1)

然后按照scanf documentation了解[^\n]的作用。

使用cpp stringification完全可以避免使用NAME_MAX_S常量...

if(scanf("%" #NAME_MAX "[^\n]", name) != 1)

答案 4 :(得分:2)

if (scanf("%" NAME_MAX_S "[^\n]", name) != 1) // This line

这是一种写作方式:

if (scanf("%80[^\n]", name) != 1)

有许多功能可能让您感到困惑。

  1. "%" NAME_MAX_S "[^\n]"表示法使用字符串连接从片段创建单个字符串。
  2. %80[^\n]转换规范使用否定的'扫描集'来指定读取的字符串最多可以包含80个非换行符。
  3. scanf()%s中指定%[]等的长度时,指定空字节(旧设计)之外的字符数。这意味着您必须处理字符串变量的已定义大小与转换规范中指定的长度之间的逐个差异。
  4. 整体条件正确检查是否已成功读取字符串。它将检测无法转换任何内容(返回值0,因为输入中的第一个字符是换行符)以及EOF。唯一可能的问题是,它没有保留返回值来区分两者,但您可以使用feof()ferror()这样做 - 这就是他们的意图(区分错误)在事情失败之后)。
  5. 代码使用NAME_MAX_S来确保它有一个字符串。它本可以使用:

    #define STR_EVALUATE(x) #x
    #define STRINGIFY(x) STR_EVALUATE(x)
    
    if (scanf("%" STRINGIFY(NAME_MAX) "[^\n]", name) != 1)
    

    这会将要维护的行数减少到1.但它只适用于简单的数字;如果你有一个表达式#define NAME_MAX (2*LEN_NAME_COMPONENT+LEN_MIDDLE_INITIALS),字符串形成的整个过程将不起作用。然后你需要做:

    char format[16];
    sprintf(format, "%%%d[^\n]", NAME_MAX);
    
    if (scanf(format, name) != 1)