RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1562421
Accepted
michk4
michk4
Asked:2024-01-20 16:38:27 +0000 UTC2024-01-20 16:38:27 +0000 UTC 2024-01-20 16:38:27 +0000 UTC

如何在不知道 C 宏中的类型的情况下输出 __VA_ARGS__

  • 772

如何在不知道宏参数类型的情况下使用 fprintf() 显示宏参数 __VA_ARGS__ 的值

#define LOG(M, ...) fprintf(stderr, "[INFO] %s [%d]: %s /*__VA_ARGS__?*/\n", __FILE__, __LINE__, M)
c
  • 5 5 个回答
  • 176 Views

5 个回答

  • Voted
  1. Best Answer
    Stanislav Volodarskiy
    2024-01-21T01:20:53Z2024-01-21T01:20:53Z

    标准溶液。有一些类型,它们隐藏在模板行中:

    #include <stdio.h>
    
    #define LOG(...)                                              \
        do {                                                      \
            fprintf(stderr, "INFO %s:%d: ", __FILE__, __LINE__);  \
            fprintf(stderr, __VA_ARGS__);                         \
            fputc('\n', stderr);                                  \
        } while (0)
    
    int main(int argc, char *argv[]) {
        LOG("start");
        LOG("argc = %d, argv = %p", argc, (void *)argv);
    }
    
    $ gcc -std=c11 -pedantic -Wall -Wextra -Werror -Wwrite-strings -Wconversion var-args.c
    
    $ ./a.out 
    INFO var-args.c:13: start
    INFO var-args.c:14: argc = 1, argv = 0x7fff8c0e1a88
    

    PS编译器检查模板和参数是否匹配。如果从 中删除强制转换(void *)argv,将会出现诊断错误:

    $ gcc -std=c11 -pedantic -Wall -Wextra -Werror -Wwrite-strings -Wconversion var-args.c
    var-args.c: In function ‘main’:
    var-args.c:14:9: error: format ‘%p’ expects argument of type ‘void *’, but argument 4 has type ‘char **’ [-Werror=format=]
       14 |     LOG("argc = %d, argv = %p", argc, argv);
          |         ^~~~~~~~~~~~~~~~~~~~~~        ~~~~
          |                                       |
          |                                       char **
    var-args.c:8:25: note: in definition of macro ‘LOG’
        8 |         fprintf(stderr, __VA_ARGS__);                         \
          |                         ^~~~~~~~~~~
    var-args.c:14:29: note: format string is defined here
       14 |     LOG("argc = %d, argv = %p", argc, argv);
          |                            ~^
          |                             |
          |                             void *
    cc1: all warnings being treated as errors
    
    • 3
  2. avp
    2024-01-20T23:22:26Z2024-01-20T23:22:26Z

    根据可变参数宏的描述,最简单的方法可能是编写和使用类似的宏,如下所示:

    #include <stdio.h>
    #include <stdlib.h>
    
    
    #define LOG(M, FMT...) ({                                               \
          int l = fprintf(stderr, "[INFO] %s [%d]: %s ",                    \
                          __FILE__, __LINE__, M);                           \
          if (l > 0) {                                                      \
            l += fprintf(stderr, FMT);                                      \
            fputc('\n', stderr);                                            \
            ++l;                                                            \
          }                                                                 \
          l; })
    
    
    int
    main (int ac, char *av[])
    {
    
      LOG("Start", " ");
      LOG("Args:", "ac = %d av = %p", ac, av);
    
      return puts("End") == EOF;
    }
    

    编译并运行

    avp@avp-desktop:~/avp/hashcode$ gcc t1.c && ./a.out 1 2 3
    [INFO] t1.c [20]: Start  
    [INFO] t1.c [21]: Args: ac = 4 av = 0x7ffc2f503168
    End
    avp@avp-desktop:~/avp/hashcode$ 
    
    • 2
  3. Stanislav Volodarskiy
    2024-01-21T03:37:48Z2024-01-21T03:37:48Z

    是的,您可以在不知道值类型的情况下打印值。出现在C11 generic selection。使用它,编译器根据参数的类型选择一个函数。C 中没有函数重载,但generic selection允许您模拟它。

    该宏DUMP打印三种类型中任意一种的值:int、char *和const char *。您可以添加更多类型,我将自己限制为三种作为示例。

    宏DUMP1——DUMP9扩展为调用链DUMP。每个宏根据其编号处理参数。例如:DUMP4(1, 2, 3, 4)将在以下时间打开DUMP(1); DUMP(2); DUMP(3); DUMP(4);

    该宏DUMPX选择数字宏之一并将其应用于其参数。例如,DUMPX(1, 2, 3, 4)它将在 中打开DUMP(1); DUMP(2); DUMP(3); DUMP(4);。

    该宏LOG输出一到九个参数。

    这是 C11 的完全标准代码。如果您决定使用它,我建议为除 之外的所有宏添加前缀LOG,以减少与其他库重叠的机会。例如:LOG_IMPL_DUMP,LOG_IMPL_DUMP1- LOG_IMPL_DUMP9,LOG_IMPL_DUMPX,LOG_IMPL_TENTH。

    #include <stdio.h>
    
    void dump_int(int v) { fprintf(stderr, " %d", v); }
    void dump_pchar(const char *v) { fprintf(stderr, " %s", v); }
    
    #define DUMP(X)                   \
        _Generic(                     \
            (X)                     , \
            int         : dump_int  , \
                  char *: dump_pchar, \
            const char *: dump_pchar  \
        )(X)
    
    #define TENTH(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) _10
    
    #define DUMP1(V1) DUMP(V1);
    #define DUMP2(V1, ...) DUMP1(V1) DUMP1(__VA_ARGS__)
    #define DUMP3(V1, ...) DUMP1(V1) DUMP2(__VA_ARGS__)
    #define DUMP4(V1, ...) DUMP1(V1) DUMP3(__VA_ARGS__)
    #define DUMP5(V1, ...) DUMP1(V1) DUMP4(__VA_ARGS__)
    #define DUMP6(V1, ...) DUMP1(V1) DUMP5(__VA_ARGS__)
    #define DUMP7(V1, ...) DUMP1(V1) DUMP6(__VA_ARGS__)
    #define DUMP8(V1, ...) DUMP1(V1) DUMP7(__VA_ARGS__)
    #define DUMP9(V1, ...) DUMP1(V1) DUMP8(__VA_ARGS__)
    
    #define DUMPX(...) \
        TENTH(__VA_ARGS__, DUMP9, DUMP8, DUMP7, DUMP6, DUMP5, DUMP4, DUMP3, DUMP2, DUMP1, _)(__VA_ARGS__)
    
    #define LOG(...)                                             \
        do {                                                     \
            fprintf(stderr, "INFO %s:%d:", __FILE__, __LINE__);  \
            DUMPX(__VA_ARGS__)                                   \
            fputc('\n', stderr);                                 \
        } while (0)
    
    int main(int argc, char *argv[]) {
        LOG("start");
        LOG("argc =", argc, "argv[0] =", argv[0]);
        LOG(1, 2, 3, 4, 5, 6, 7, 8, 9);
    }
    
    $ gcc -std=c11 -pedantic -Wall -Wextra -Werror -Wwrite-strings -Wconversion var-args.c
    
    $ ./a.out 
    INFO var-args.c:39: start
    INFO var-args.c:40: argc = 1 argv[0] = ./a.out
    INFO var-args.c:41: 1 2 3 4 5 6 7 8 9
    

    PS在我看来,使用类似的工具,我们可以根据参数类型组装一种格式并构造一个函数调用fprintf。现在有很多,将来也会有一个。

    PS可以修改宏DUMP,以便它打印参数名称LOG及其值。需要编写的调试代码更少。例如:LOG(argc, argv[0]);将打印argc = 1 argv[0] = ./a.out.

    • 2
  4. Pak Uula
    2024-01-21T00:17:28Z2024-01-21T00:17:28Z

    我不仅会这样做LOG(M,...),LOG(FMT,...)还会FMT指定输出的格式。

    #include <stdarg.h>
    #include <stdio.h>
    
    #define LOG(FMT, ...) fprintf(stderr, "[INFO] %s [%d]: " FMT "\n", __FILE__, __LINE__ __VA_OPT__(, ) __VA_ARGS__)
    
    int main(int argc, char *argv[])
    {
        LOG("Start");
        LOG("Args: argc == %d, argv == %p", argc, argv);
        // Компилятор сообщит об ошибке: format ‘%d’ expects a matching ‘int’ argument ... in expansion of macro ‘LOG’
        // LOG("Mismatch: argc == %d, argv == %p");
    }
    

    结果:

    [INFO] some.c [8]: Start
    [INFO] some.c [9]: Args: argc == 1, argv == 0x7ffc2cc8f148
    
    • 0
  5. AVI-crak Home
    2024-01-22T16:09:40Z2024-01-22T16:09:40Z

    我不明白将标准 printf() 子集函数与 _Generic() 宏一起使用的意义。这就像买了机票然后步行去一样。这是完成的 printo("text", double, float, uint(8-16-32-64)_t, int(8-16-32-64)_t ) https://github.com/AVI-crak/ Rtos_cortex/树/master/printo

    • 0

相关问题

  • free 出于某种原因不会从内存中删除数组

  • 请帮助代码

  • 为什么 masm 对字符串或文本文字太长发誓,为什么在结构中设置 db 或 dw?

  • 如何将数字拆分为位并将其写入 C 中的数组?

  • 如何以给定的角度移动物体?

  • 解决“子集和问题”的时效算法

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

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

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5