【问题标题】:get a float number from a void* [closed]从 void* 获取浮点数 [关闭]
【发布时间】:2017-12-20 09:57:07
【问题描述】:

我正在尝试重新编程 printf 函数(大学项目),但 %f 给我带来了问题。 我像这样使用函数指针:

void (*fct[15])(void*) =
            {printfs, printfc, printfd, printfd, printfl, printfx, printfX,
             printfo, printfb, printfS, printfu, printfp, printff};

当使用正确的标志时,这些函数之一被调用。 一个典型的 printfd 将接收一个 void* 变量作为参数并且会这样做:

int nbr = (int)var;     //var is a void* type variable
my_put_nbr(var);        //handmade function wich displays numbers

问题是,出于某种奇怪的原因,当我对 printff 执行相同操作时,它不起作用

float nbr = (float)var;

我收到消息

错误:指针值在预期的浮点值处使用 float nbr = (float)var

我也尝试过这样的事情。如果我有

my_printf("%f", var);     //with var = 12.34

然后我调用应该执行的函数 printff():

int nbr1 = (int)(var / 1);
int nbr2 = (int)(var % 1);

所以我通常会有 nbr1 = 12 和 nbr2 = 34,然后我会在两者之间显示“。”但它仍然不起作用,我收到消息:

错误:二进制 * 的无效操作数(具有“void *”和“int”)int nbr1 = (int)(var*1)

我不知道,所以如果你有任何线索,请告诉我。 另外,我不能使用 printf() 之类的命令,这是本练习的规则,所以不要告诉我解决方案隐藏在这里。

【问题讨论】:

  • 您需要添加一个minimal reproducible example,因为到目前为止确实没有足够的代码来了解您在做什么。虽然猜测,看起来你应该研究变量参数列表。
  • 您正在尝试将指针大小写为正常值!
  • 顺便说一句,类似 printf 的函数永远永远不会获得浮点数。只有双。请注意,在标准库 printf 中,%f 需要一个双参数。浮点数没有输出格式规范,你有没有想过为什么?
  • 不要尝试在评论中粘贴您的代码 - 编辑您的问题!
  • 您对 va_arg 的使用不正确并导致未定义的行为。你必须重新设计你的整个方法。它似乎适用于您计算机上的单个 int 参数这一事实纯属意外,也是不幸的,因为它导致您在死胡同上浪费了很多时间。

标签: c pointers variable-assignment


【解决方案1】:

如果我正确理解了您的问题,当您完全确定调用者部分时,在被调用函数中,考虑到您收到var 中的参数,您可以这样做

  • 对于int

    printf("%d", *(int *)var);
    
  • 对于float/double

    printf("%f", *(double *)var);
    

等等。

请注意,不存在可变参数函数的 float 类型,因为对于这些函数,由于默认参数提升,float 类型被提升为 double

详细说明,您不会将传入指针强制转换/分配给所需的类型(即无效的 [类型不匹配] 和不需要的)。相反,您将 void * 参数转换为适当的调用方参数类型(基于您提到的标志),然后取消引用以获取值。

这个类比是基于C11,第 6.3.2.3 章中提到的属性

指向void 的指针可以转换为或从指向任何对象类型的指针转​​换。指向的指针 任何对象类型都可以转换为指向void 的指针并再次返回;结果应 比较等于原始指针。

【讨论】:

  • 我很想这样做,但我不是使用该功能的人,真的。我们有一台计算机来测试我们的程序,因此调用 my_printf() 的方式必须与我们通常调用 printf() 的方式相匹配。这意味着我必须写 my_printf("%d", nbr);而不是 my_printf("%d", (int)nbr);
  • 这个答案将受益于提到可变参数float 参数被提升为double
  • @KlasLindbäck 如果您认为它有用,让我补充一下,毕竟它没有害处。 :)
【解决方案2】:

这叫做双关语。指针双关语是UB。最安全的方法是将数据从 void 指针 memcpy 到 float 变量。

float x; memcpy (&x, voidptr, sizeof(float));

【讨论】:

    【解决方案3】:

    您没有正确调用va_args。第二个参数必须是您希望从变量参数中读取的数据类型,因为va_args 需要正确的大小才能知道从哪里获取下一个参数。

    虽然您使用数组来存储所有可能的函数可能看起来很聪明,但如果有一种更简单、更易读的方法,您实际上会让事情变得更难。它可能也更具可扩展性。

    switch(format_char)
      {
    case 'd':
      print_an_int(va_args(args,int));
    break;
    case 'f':
      print_an_float(va_args(args,float));
    break;
      }
    

    【讨论】:

      【解决方案4】:

      尝试使用

      int nbr = *((int *)var); 
      float nbr = *((float *)var);
      

      将 void 指针 var 转换为整数/浮点指针,然后获取存储在该内存地址中的值并将值存储到变量中

      而不是

      int nbr = (int)var;

      float nbr = (float)var;

      将存储在 var 中的地址转换为 int/float。在这里,您只是转换地址,而不是存储在该地址中的值。

      【讨论】:

        【解决方案5】:

        与其尝试从args 提取参数并将结果传递给fct[i],不如直接将args 传递给fct[i] 并让它们进行提取。唯一需要注意的是,您不能按值传递。

            fct[i](&args);
        

        在每个 printf* 辅助函数中执行以下操作:

        void printf_double (va_list *args) {      
           double val = va_arg(*args, double);
           // print it
        }
        

        注意float 不是printf 的有效类型,因为float 在可变参数调用中被提升为double

        【讨论】:

          猜你喜欢
          • 2011-10-05
          • 2020-08-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多