【问题标题】:_Generic macro with void* pointers doesn't take a double argument?_带有 void* 指针的通用宏不接受双参数?
【发布时间】:2018-04-14 09:29:36
【问题描述】:

我尝试实现 _Generic 宏,它可以采用任何数据类型并对其进行处理,此示例工作正常,但是使用 2 个通用宏,我如何仅使用一个具有 char* int 和 double 的 Generic 来实现它?

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

  #define puts(x) _Generic((x),              \
    char*:    _puts((void*) x, "str"),       \
    int:      _puts((void*) x, "int")        \
  )

  #define putsd(x) _Generic((x),\
    double: _puts((void*)to_s(x), "float") \
  )


  char*
  to_s(double x)
  {
    char* str = (char *)malloc(1502);
    sprintf(str, "%lf", x);
    free(str);
    return str;
  }

  void _puts(void* d, const char* type)
  {
    if (strcmp(type, "int") == 0){
      printf("%d\n", d);
    } else if (strcmp(type, "float") == 0){
      double res;
      sscanf(d, "%lf", &res);
      printf("%lf\n", res);
    } else {
      printf("%s\n", d);
    }
  }


  int main(void) {
    puts("String");
    puts(1230);
    putsd(13.37);

    return 0;
  }

另外,当我尝试跟随时,我得到一个错误“'to_s' puts("String"); 的参数 1 的类型不兼容:

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

  #define puts(x)                              \
    _Generic((x),                               \
        char*:    _puts((void*) x, "str"),       \
        int:      _puts((void*) x, "int"),        \
         double : _puts((void *)to_s(x), "float") \
        )


  char *to_s(double x) {
    char *str = (char *)malloc(1502);
    sprintf(str, "%lf", x);
    free(str);
    return str;
  }

  void _puts(void *d, const char *type) {
    if (strcmp(type, "int") == 0) {
        printf("%d\n", d);
    } else if (type == "float") {
        double res;
        sscanf(d, "%lf", &res);
        printf("%lf\n", res);
    } else {
        printf("%s\n", d);
    }
  }

  int main(void) {
    puts("String");
    puts(1230);
    puts(13.37);

    return 0;
  }

【问题讨论】:

    标签: c macros void


    【解决方案1】:

    你不能用==比较字符串

    改变

    if (type == "int") {
    

    if (strcmp(type, "int") == 0) {
    

    float 也一样...

    另一方面,你不能传递像123013.39这样的文字地址,使用中间指针:

    int i = 1230;
    int *pi = &i;
    
    puts(pi);
    

    然后在你的通用宏中:

    #define puts(x)                    \
        _Generic((x),                   \
            char*:    _puts((x), "str"), \
            int *:    _puts((x), "int"),  \
            double *: _puts((x), "float")  \
    )
    

    GLib 使用一些丑陋的技巧将文字(转换为 long)作为指针传递,但这不是可移植的:Type Conversion Macros

    另外,请注意puts 是标准库中函数的名称,覆盖此名称不是一个好主意。

    您的代码使用复合文字更正以传递指针:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define Puts(x)                \
        _Generic((x),               \
        char *:   _Puts((x), "str"), \
        int *:    _Puts((x), "int"),  \
        double *: _Puts((x), "float")  \
    )
    
    static void _Puts(void *d, const char *type)
    {
        if (strcmp(type, "int") == 0) {
            printf("%d\n", *(int *)d);
        } else
        if (strcmp(type, "float") == 0) {
            printf("%f\n", *(double *)d);
        } else {
            printf("%s\n", (char *)d);
        }
    }
    
    int main(void)
    {
        Puts("String");
        Puts((int []){1230});
        Puts((double []) {13.37});   
        return 0;
    }
    

    【讨论】:

    • 感谢您的更正和帮助,但是当我更改为 int: _puts(&x, "int") 时出现错误:“lvalue required as unary '&' operand”,这有什么问题?
    • 这种方法会导致意外的 0.000 值而不是初始双精度值,也许我错过了什么?感谢您的帮助,我解决了您指出的所有问题。 ideone.com/jOp2wA
    • @Partylover,是的,因为您不能使用double 作为fscanf 的第一个参数,所以您可以对代码进行更正的编辑。
    【解决方案2】:

    您错过了泛型的全部意义,您创建了一个与泛型完全相同的函数(又名。_puts()。您应该直接在泛型中使用正确的函数,加上to_s() 免费str.. .然后你使用它!调用未定义的行为。不要使用stdlib的保留函数名puts()

    一个很好的例子是here

    #include <stdio.h>
    
    #define printf_dec_format(x) _Generic((x), \
        char: "%c", \
        signed char: "%hhd", \
        unsigned char: "%hhu", \
        signed short: "%hd", \
        unsigned short: "%hu", \
        signed int: "%d", \
        unsigned int: "%u", \
        long int: "%ld", \
        unsigned long int: "%lu", \
        long long int: "%lld", \
        unsigned long long int: "%llu", \
        float: "%f", \
        double: "%f", \
        long double: "%Lf", \
        char *: "%s", \
        void *: "%p")
    
    #define print(x) printf(printf_dec_format(x), x)
    #define printnl(x) printf(printf_dec_format(x), x), printf("\n");
    
    int main(void) {
        printnl('a');    // prints "97" (on an ASCII system)
        printnl((char)'a');  // prints "a"
        printnl(123);    // prints "123"
        printnl(1.234);      // prints "1.234000"
        printnl("hello");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-01
      • 2021-05-09
      • 2013-12-18
      • 2015-12-05
      • 2014-09-05
      • 2018-08-25
      • 2012-08-21
      相关资源
      最近更新 更多