AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 76925335
Accepted
JNeruda
JNeruda
Asked: 2023-08-18 05:53:15 +0800 CST2023-08-18 05:53:15 +0800 CST 2023-08-18 05:53:15 +0800 CST

在 C 中的 scanf() 格式说明符中使用宏获取字符串长度

  • 772

9是否可以用宏替换数字以使这行代码具有更多的代码可维护性?

scanf("%9[^\n]s", str);

我尝试阅读文档,但找不到这些操作的确切名称:

  1. "[^\n]s"

  2. "%ns"

我尝试了这些替代方案,但 Clion 将两行中第一次出现的标记str为错误:

scanf("%" str(MAX_LENGTH) "%[^\n]s", str);

scanf("%" str(MAX_LENGTH) "[^\n]%*c", str);
c
  • 2 2 个回答
  • 58 Views

2 个回答

  • Voted
  1. Best Answer
    chqrlie
    2023-08-18T06:25:43+08:002023-08-18T06:25:43+08:00

    您收到错误的原因不是str内置函数也不是预定义的宏。您可以定义str为宏并使用字符串化运算符#来执行替换,但这很棘手且令人困惑:str必须定义为调用另一个宏的宏xstr,而该宏又将其参数字符串化为#x:

    #define xstr(x)  #x
    #define str(x)  xstr(x)
    

    但请注意,您的两个示例都有问题:

    • scanf("%" str(MAX_LENGTH) "%[^\n]s", str);s格式末尾有一个额外的内容,这是无用的,并且表明%[...]转换 和之间存在混淆%s,两者都需要字符计数前缀以防止缓冲区溢出。第二个%也是不正确的。str此外,您不应该对宏和目标数组使用相同的标识符:虽然不是错误,但它会使代码不必要地混乱。代码应该写成:

        char buf[MAX_LENGTH + 1];
        scanf("%" str(MAX_LENGTH) "[^\n]", buf);
      
    • scanf("%" str(MAX_LENGTH) "[^\n]%*c", str);MAX_LENGTH具有正确的形式,但将无条件消耗匹配后的下一个字节,如果该行在换行符之前有多个字节,则该字节不是换行符。不会向调用者返回任何指示。

    • %9[^\n]在空输入行上将失败,因为没有字符与转换规范匹配。scanf()将返回0并使目标数组处于未确定状态。

    这是一个简短的例子:

    #include <stdio.h>
    
    #define MAX_LENGTH  9
    
    #define xstr(x)  #x
    #define str(x)  xstr(x)
    
    int main(void) {
        char buf[MAX_LENGTH + 1];
        if (scanf("%" str(MAX_LENGTH) "[^\n]", buf) == 1) {
            printf("got |%s|\n", buf);
        } else {
            printf("invalid input\n");
        }
        return 0;
    }
    

    如果str被定义为#define str(x) #x,调用str(MAX_LENGTH)将扩展为"MAX_LENGTH"。第二个宏调用在第一次扩展初始宏参数后执行其替换,因此str(MAX_LENGTH)扩展为xstr(9),它扩展为"9"。

    还要注意,这MAX_LENGTH不是目标数组的长度:必须为空终止符添加一个额外的字符,并且宏调用中没有一致性检查:MAX_LENGTH和定义之间的一致性buf完全依赖于程序员。

    此外,如果 的定义MAX_LENGTH不是没有后缀的整数常量,则此宏扩展技巧将无法生成正确的scanf转换说明符。

    更可靠的方法是snprintf构造scanf()格式字符串:

    #include <stdio.h>
    
    #define MAX_LENGTH  9
    
    int main(void) {
        char buf[MAX_LENGTH + 1];
        char format[20];
        snprintf(format, sizeof format, "%%%zu[^\n]", sizeof(buf) - 1);
        if (scanf(format, buf) == 1) {
            printf("got |%s|\n", buf);
        } else {
            printf("invalid input\n");
        }
        return 0;
    }
    

    这个版本效果更好,但也有自己的缺点:它阻止编译器检查格式字符串和其余参数之间的一致性scanf(),这将导致在建议的警告级别( )下发出警告-Wall -Wextra,并且这种一致性检查非常有用,而格式字符串构造格式字符串很容易出错。

    最后,这两种方法都很麻烦并且容易出错。fgets()用于您的目的并手动删除尾随换行符要可靠得多:

    #include <stdio.h>
    #include <string.h>
    
    #define MAX_LENGTH  9
    
    int main(void) {
        char buf[MAX_LENGTH + 2];
        if (fgets(buf, sizeof buf, stdin)) {
            buf[strcspn(buf, "\n")] = '\0';
            printf("got |%s|\n", buf);
        } else {
            printf("no input\n");
        }
        return 0;
    }
    

    行为略有不同:fgets除非行太长,否则将消耗换行符,这会使错误恢复更加困难。

    总体而言,更好的解决方案似乎是使用自定义函数:

    #include <stdio.h>
    
    #define MAX_LENGTH  9
    
    /* read a line from a stream and truncate excess characters */
    int get_line(char *dest, int size, FILE *fp) {
        int c;
        int i = 0;
        while ((c = getc(fp)) != EOF && c != '\n') {
            if (i + 1 < size)
                dest[i] = c;
            i++;
        }
        if (i < size) {
            dest[i] = '\0';
        } else
        if (size > 0) {
            dest[size - 1] = '\0';
        }
        return (i == 0 && c == EOF) ? -1 : i;
    }
    
    int main(void) {
        char buf[MAX_LENGTH + 1];
        if (get_line(buf, sizeof buf, stdin) == EOF) {
            printf("invalid input\n");
        } else {
            printf("got |%s|\n", buf);
        }
        return 0;
    }
    

    请注意,该行为仍然与原始scanf()调用略有不同,但可能更接近您的目标:

    • get_line读取整行,包括换行符,多余的字符将被丢弃。
    • get_line如果不为 0,则始终将 C 字符串存储到目标数组中size,即使在文件末尾处buf也是空字符串。 scanf()将EOF在文件末尾返回并保持buf不变。
    • get_line将接受空行,而scanf()会失败、返回0并buf处于不确定状态,这是您可能没有意识到的限制。

    结论:scanf()充满了怪癖。尝试使用显式字符计数来避免缓冲区溢出是一个好主意,但scanf()会导致其他不易处理的问题。通常需要编写自定义代码才能获得精确且一致的语义。

    • 4
  2. Jonathan Leffler
    2023-08-18T06:35:02+08:002023-08-18T06:35:02+08:00

    仅当要扩展的宏是一个简单的数字(例如,不是涉及加法或减法的更通用的表达式)时,才可以使用字符串化。

    然后,您必须使用正常的双宏舞蹈才能正确扩展宏:

    #define MAX_LENGTH 32
    #define EXPAND_STR(x) #x
    #define STR(x) EXPAND_STR(x)
    

    进而:

    char buffer[MAX_LENGTH + 1];
    
    if (scanf("%" STR(MAX_LENGTH) "[^\n]", buffer) == 1)
        …success…
    
    • 1

相关问题

  • 如何将#define的数据类型设置为long double?

  • 不兼容的常量指针

  • OpenGL 中的非渐变颜色变化

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    使用 <font color="#xxx"> 突出显示 html 中的代码

    • 2 个回答
  • Marko Smith

    为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类?

    • 1 个回答
  • Marko Smith

    您可以使用花括号初始化列表作为(默认)模板参数吗?

    • 2 个回答
  • Marko Smith

    为什么列表推导式在内部创建一个函数?

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 个回答
  • Marko Smith

    为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)?

    • 4 个回答
  • Marko Smith

    为什么库中不调用全局变量的构造函数?

    • 1 个回答
  • Marko Smith

    std::common_reference_with 在元组上的行为不一致。哪个是对的?

    • 1 个回答
  • Marko Smith

    C++17 中 std::byte 只能按位运算?

    • 1 个回答
  • Martin Hope
    fbrereto 为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 您可以使用花括号初始化列表作为(默认)模板参数吗? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi 为什么列表推导式在内部创建一个函数? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A fmt 格式 %H:%M:%S 不带小数 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python C++20 的 std::views::filter 未正确过滤视图 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute 为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa 为什么库中不调用全局变量的构造函数? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis std::common_reference_with 在元组上的行为不一致。哪个是对的? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev 为什么编译器在这里错过矢量化? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan C++17 中 std::byte 只能按位运算? 2023-08-17 17:13:58 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve