首页 文章

printf由给定的指针和格式字符串组成 . 浮动问题

提问于
浏览
5

因为我想跟踪一些变量以便看到它们如何变化,我想创建一个函数,它接收一个格式字符串,具体取决于变量的类型和指向值的指针 . 我希望,在格式字符串的帮助下,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指针强制转换为float):

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

结果是:

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

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

但是,浮动的问题仍然存在 . 为了打印浮点值,我需要将指针强制转换为float,但没有别的 . 相反:当我转换为浮动时,除浮动之外的所有事情都会失败 . 是不是与读取数据有关的取消引用,位于指向的地址并将其传递给printf? (相同的指针,转换为任何类型保持相同的地址?)然后,格式字符串能够以适当的格式“读取”给定的字节 - char,int,unsigned int等 .

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

总之,我有两个问题 .

  • 为什么浮点数与所有其他类型的行为不同 .

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

谢谢 .

4 回答

  • 4
    • 浮点数的行为有所不同,因为格式指定器期望32位大小的类型,并且您发送的是64位大小的类型 . 对于其他类型,你很幸运有endianness .

    • 列出错误使用库函数的副作用是没有意义的 .

    修复代码 . 我会看一下 vprintf()vsprintf() 函数,或者更好地说明你的设计目的,可能有一个比你所展示的代码更简单,更可行的解决方案 .

  • 4

    这既没有回答您的问题,但如果您尝试包装printf,可能需要查看vprintf .

  • 1

    你很幸运(或不幸)打印了你对这么多类型的预期 .

    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
    
  • 4

    您尝试使用的方法甚至无法实现 . 它不能这样做 . 如果你坚持"a format string and a pointer"接口为您的 printf 包装功能,然后让它的工作是手工解析格式字符串的唯一途径,确定实际的参数类型和投(和反引用)与适当类型的指针值 . 在这种情况下,没有"one cast fits all"解决方案 .

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

    同样,使其工作的唯一方法是手动解析格式字符串,这也不是一个好主意 . 如果您可以解释“格式字符串和指针”界面的来源,也许我们可以建议一个更合理的界面 .

相关问题