Printf疯狂了

dit*_*lav 7 c++ struct operator-overloading

我的源代码中有很多代码,但主要问题是连续2行.

 struct step
 {
     int left,tonum;
     long long int rez;
 };

inline bool operator==(const step& a, const step& b)
{
    printf("\n%d",b.tonum);
    printf("\n%d %d | %d %d | %d %d",a.left, b.left, a.rez, b.rez, a.tonum, b.tonum);
    return a.left==b.left && a.rez==b.rez && a.tonum==b.tonum;
}
Run Code Online (Sandbox Code Playgroud)

这被称为相当几百万次,但问题是虽然大多数时候它应该是相同的,但它永远不会,并且输出确实非常奇怪.

2
7989 7989 | 53 0 | 53 0
1
8989 7989 | 52 0 | 53 0
2
8989 8989 | 52 0 | 52 0
1
7899 8989 | 51 0 | 52 0
Run Code Online (Sandbox Code Playgroud)

不仅应该b.tonum是相同的,而且他应该== a.tonum出于其他一些原因,这些原因在这段代码中没有解释

为什么b.tonum两次打印都不一样?

Chr*_*ung 10

你不能%d用来打印long long.你必须使用%lld.(因此请使用"\n%d %d | %lld %lld | %d %d"您的格式字符串.)

特别是,很明显在"52 0 | 52 0"中,第一个52 0是a.rez,第二个52 0是b.rez(这些中的每一个都是a long long,显然(从输出判断)将两个字推入堆栈).a.tonum并且b.tonum根本不打印.


要理解为什么会发生这种情况,让我解释一下乔纳森和我想说的话.当您调用类似的变量函数printf(声明为类似的函数时printf(const char *format, ...),编译器无法...在编译时验证正确的参数类型.因此,在这种情况下,有一个过程用于决定在堆栈上推送什么,可粗略地概括为:如果它是int或可推广的int,它被推送为int;如果它是double或可推广的double,它被推送为a double;否则,它被按原样推送.

在实现类似的可变参数函数时printf,您需要一些方法来访问这些...项.这样做的方法是使用a va_list,声明在<stdarg.h>.这是一些伪代码,显示它是如何使用的:

int printf(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    while (/* we find the next format marker */) {
        if (/* %d, %i, %c */) {
            int val = va_arg(ap, int);
            /* print out val as decimal or (for %c) char */
        } else if (/* %u, %x, %X, %o */) {
            unsigned int val = va_arg(ap, unsigned int);
            /* print out val as decimal, hex, or octal */
        } else if (/* %ld, %li */) {
            long val = va_arg(ap, long);
            /* print out val as decimal */
        } else if (/* %lu, %lx, %lX, %lo */) {
            unsigned long val = va_arg(ap, unsigned long);
            /* print out val as decimal, hex, or octal */
        } else if (/* %lld, %lli */) {
            long long val = va_arg(ap, long long);
            /* print out val as decimal */
        } else if (/* %llu, %llx, %llX, %llo */) {
            unsigned long long val = va_arg(ap, unsigned long long);
            /* print out val as decimal, hex, or octal */
        } else if (/* %s */) {
            const char *val = va_arg(ap, const char *);
            /* print out val as null-terminated string */
        } /* other types */
    }
    va_end(ap);
    return /* ... */;
}
Run Code Online (Sandbox Code Playgroud)

请注意,每次要选择一个...参数时,va_arg都要使用,并且必须指定要选择的类型.您可以选择正确的类型.如果类型不正确,则会出现类型错误,在大多数情况下会出现未定义的行为(意味着程序可以执行任何喜欢的操作,包括崩溃或更糟).

在您的特定计算机中,似乎当您通过a时long long,它将64位数量推送到堆栈,但由于您使用了%d格式说明符,因此它使用的va_arg(ap, int)版本只获取了32位数量.这意味着64位字的另一半仍然未读,随后%d继续阅读.这就是为什么在格式字符串完成时,它永远不会处理您传递的值a.tonumb.tonum.

如果你正确使用%lld它,它会使用va_arg(ap, long long),并且可以正确读取整个64位数量.

  • 为什么要使用`printf`? (4认同)
  • 由于代码调用了未定义的行为,因此任何输出都是有效的.但是,输出53和0两次与两个8字节"long long"值被推送和读取为四个4字节值一致,并且被推送为"a.tonum"和"b.tonum"的值被忽略. (3认同)