普林德夫疯了
Printf gone wild
我的源代码中有很多代码,但主要问题是连续 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;
}
这被调用了几百万次,但问题是,尽管大多数时候它应该是相同的,但它永远不会,而且输出确实非常奇怪。
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
不仅b.tonum
应该是相同的,而且由于这段代码中未解释的其他一些原因,他也应该被== a.tonum
为什么b.tonum
两次打印都不一样?
不能使用%d
打印long long
。你必须使用%lld
.(因此,请使用"n%d %d | %lld %lld | %d %d"
作为格式字符串。
特别是,很明显,在"52 0 |52 0",第一个 52 0 是a.rez
,第二个 52 0 是b.rez
(每个,作为一个long long
,显然(从输出来看)将两个单词推到堆栈中)。a.tonum
和b.tonum
根本没有打印。
为了理解为什么会发生这种情况,让我解释一下乔纳森和我想说的话。当你调用像printf
这样的可变参数函数(声明为类似printf(const char *format, ...)
)时,编译器无法在编译时验证...
的正确参数类型。因此,在这种情况下,有一个程序可以决定在堆栈上推送什么,大致可以总结为:如果它int
或可提升到int
,则将其作为int
推送;如果它double
或可推广到double
,它就会被推送为double
;否则,它将被按原样推送。
当实现像printf
这样的可变参数函数时,你需要某种方法来访问...
项。这样做的方法是使用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 /* ... */;
}
请注意,每次要选取...
参数时,都会使用va_arg
执行此操作,并且必须指定要选取的类型。由您来选择正确的类型。如果类型不正确,则会出现类型双关情况,在大多数情况下具有未定义的行为(这意味着程序可以做任何它喜欢的事情,包括崩溃或更糟)。
在您的特定计算机中,似乎当您传递long long
时,它将 64 位数量推送到堆栈,但由于您使用了%d
格式说明符,因此它使用了va_arg(ap, int)
版本,该版本仅抓取 32 位数量。这意味着 64 位字的另一半仍未读取,随后的%d
继续读取。这就是为什么在格式字符串完成时,它从未处理过您传递的a.tonum
和b.tonum
的值。
然而,如果你正确使用%lld
,它将使用va_arg(ap, long long)
,并且会正确读取整个64位数量。
- 瓦尔格林德:数学函数"Conditional jump or move depends on uninitialised value(s)"
- 为什么瓦尔格林德在不释放恶意内存后没有报告任何问题?
- 瓦尔格林德的内存泄漏使用新的
- CPP 中的瓦尔格林德和记忆泄漏:"Conditional jump or move depends on uninitialised values"
- C++程序什么都不做,但瓦尔格林德显示内存分配
- 赫尔格林德和atomic_flag
- 删除堆后对瓦尔格林德错误感到困惑
- 为什么我会"Invalid read of size 8"?(瓦尔格林德)
- 瓦尔格林德报告在 =带有嵌套shared_ptrs的运算符上的读取错误
- 瓦尔格林德:注意到但未处理的国际奥克特尔;是否有必要处理以及如何找到?
- 瓦尔格林德错误 - 地址0x0不是堆叠的 malloc'd 或自由的
- 瓦尔格林德错误来自 boost::asio
- 瓦尔格林德可能丢失了,仍然可以到达并被瓦尔格林德检测到并被抑制
- 在瓦尔格林德的泄漏摘要中使用 std::ios::sync_with_stdio(fasle) 打印时获取"still reachable"
- 瓦尔格林德"不支持arch_prctl选项"
- 有没有办法只从瓦尔格林德获得泄漏摘要?
- 瓦尔格林德 + --泄漏检查 -> 不泄漏摘要
- 修复了瓦尔格林德错误内存泄漏的错误
- 普林德夫疯了
- 瓦尔格林德 - 可能失去了警告