【问题标题】:printf by given pointer and format string. Issue with floatsprintf 通过给定的指针和格式字符串。浮动问题
【发布时间】:2011-11-13 06:00:40
【问题描述】:

由于我想跟踪一些变量以查看它们如何变化,因此我想创建一个函数,该函数接收格式字符串,具体取决于变量的类型和指向值的指针。我希望,在格式字符串的帮助下, printf 将正确地确定值。实际上它可以工作,但有一个例外 - 浮点值无法正确打印。

这是我的代码:

#include <stdio.h>

void pprint (char* fmtstr, void* p)
{
        printf(fmtstr,*(long double *)p);
}

int main (int argc, char **argv)
{
        char cval = 64;
        int ival = -534;
        unsigned int iuval = 535;
        float fval = 534.64;
        double dval = 53432.1;
        long double ldval = 534321234.134567;
        long long lval = -654321;
        unsigned long long luval = 7654321;

        pprint ("char: %hhd\n",&cval);
        pprint ("int: %d\n",&ival);
        pprint ("uint: %u\n",&iuval);
        pprint ("float: %f\n",&fval);
        pprint ("double: %f\n",&dval);
        pprint ("long double: %Lf\n",&ldval);
        pprint ("llong: %lld\n",&lval);
        pprint ("ullong: %llu\n",&luval);
        return 0;
}

结果是:

char: 64
int: -534
uint: 535
float: 0.000000
double: 53432.100000
long double: 534321234.134567
llong: -654321
ullong: 7654321

正如我们所见,除了浮点数之外,一切都打印好了。 但是,在对 pprint 函数进行一些修改(将 void 指针转换为浮点数)之后:

printf(fmtstr,*(float*)p);

结果是:

char: 0
int: 1073741824
uint: 0
float: 534.640015
double: 0.000000
long double: 0.000000
llong: -351285912010752
ullong: 4038940431088615424

现在只有浮点值被正确打印。 另一个副作用是,转换为任何其他类型会导致成​​功 printf 的类型具有较小或相同的大小。 (如果我转换为 int,它将正确打印字符,但不会打印长字符)。因此,转换为 long double 解决了这个问题,因为它的尺寸最大。

但是,浮动的问题仍然存在。为了打印浮点值,我需要将指针转换为浮点,但仅此而已。反之亦然:当我投射到浮动时,除了浮动之外的一切都失败了。解引用不是与读取数据相关,位于指向的地址并将其传递给 printf 吗? (相同的指针,转换为任何类型都拥有相同的地址?)然后,格式字符串能够以正确的格式“读取”给定的字节 - char、int、unsigned int 等。

我听说,可变参数函数中的浮点值被转换为双精度值。这与问题有关吗?此外,在某些情况下,我无法将值提供为双精度值 - 因为例如 Opengl 中使用的许多变量都是浮点数 (GLfloat)。

总的来说,我有 2 个问题。

  1. 为什么浮点数的行为不同于所有其他类型。

  2. 使用此功能会产生副作用吗?我的意思是,当打印例如一个 int 时,printf 接收 12 个字节(sizeof(long double))作为第二个参数,但只读取 4 个(“%d”格式字符串)。从这 12 个字节中,前 4 个属于我要打印的 int 值,接下来的 8 个是垃圾,printf 永远不会读取它们。这是个问题吗?

谢谢。

【问题讨论】:

  • tl;dr,但请注意,当通过可变参数传递时,浮点数会提升为双精度数。注意只有一个%f 格式说明符,没有什么“更短”的了。另外,你的文字是错误的。

标签: c pointers floating-point printf


【解决方案1】:

这不能回答您的任何问题,但如果您尝试包装 printf,您可能需要查看 vprintf

【讨论】:

    【解决方案2】:

    你很幸运(或不幸)它打印了你对这么多类型的期望。

    C 标准在 §7.19.6.1/9 中说

    如果任何参数不是相应转换规范的正确类型,则行为未定义。

    特别是,许多实现使用不同的 CPU 寄存器将浮点数和整数传递给 printf(),即使在源代码中写入了整数,“%f”也会使 printf() 访问浮点寄存器,或者反之亦然。

    例如,在我的系统上,你的程序的输出是

    char: -1
    int: 164124920
    uint: 164124916
    float: 0.000000
    double: 0.000000
    long double: 534321234.134567
    llong: 140733357512904
    ullong: 140733357512896
    

    char: -17
    int: 1200847080
    uint: 1200847076
    float: 534.640015
    double: 0.000000
    long double: 0.000000
    llong: 140734394235064
    ullong: 140734394235056
    

    【讨论】:

      【解决方案3】:

      您尝试使用的方法甚至都不可行。不能这样做。如果您坚持为 printf 包装函数使用“格式字符串和指针”接口,那么使其工作的唯一方法是手动解析格式字符串,确定参数的实际类型并强制转换(并取消引用) 具有正确类型的指针值。在这种情况下,没有“一刀切”的解决方案。

      一般情况下,您的问题没有明确的答案。您的程序具有未定义行为的原因不止一个。具体的实际行为将取决于实施情况,也可能取决于其他因素。

      同样,让它工作的唯一方法是手动解析格式字符串,这也不是一个好主意。如果您能解释一下“格式字符串和指针”接口的来源,也许我们可以提出一个更合理的接口。

      【讨论】:

        【解决方案4】:
        1. 浮点数的行为不同,因为格式说明器需要 32 位大小的类型,而您发送的是 64 位大小的类型。对于其他类型,您很幸运能够使用字节序。

        2. 列出错误使用库函数的副作用是没有实际意义的。

        修复代码。我会查看 vprintf()vsprintf() 函数,或者更好的是,说明您的设计目的,可能有比您显示的代码更简单、更可行的解决方案。

        【讨论】:

        • @nmichaels:你先说 (vprintf)!
        猜你喜欢
        • 2010-12-10
        • 2019-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-06
        • 2017-02-21
        • 1970-01-01
        相关资源
        最近更新 更多