问题描述
我正在将一些非常旧的(> 10y)C代码移植到现代Linux上.我在自定义编写的vsnprintf()包装器中遇到分段错误(显然,它的任务是检测重复的输出字符串并进行内插):
I'm porting some very old (> 10y) C code to modern Linuxes. I'm getting segmentation faults within a custom-written vsnprintf() wrapper (apparently its task is to detect duplicate output strings and intern them):
char* strVPrintf(const String fmt, va_list ap)
{
/* Guess we need no more than 50 bytes. */
int n, size = 50;
char* p = (char*)memMalloc(size), q;
while (1) {
/* Try to print in the allocated space. */
n = vsnprintf(p, size, fmt, ap);
/* If that worked, return the string. */
if (n > -1 && n < size) {
break;
}
/* Else try again with more space. */
if (n > -1) /* glibc 2.1 */
size = n + 1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
p = memRealloc(p, size);
}
q = strRegister(p);
memFree(p);
return q;
}
作者似乎已经假设标准的vsnprintf()
函数返回写入的字符数,并且如果没有收到足够的空间来格式化所有args,则仅返回一个哨兵值.这意味着您可以猜测缓冲区的大小,并在必要时增加缓冲区大小.
The author seems to have assumed that the standard vsnprintf()
function returns the number of characters written, and simply returns a sentinel value if it doesn't receive enough space to format all args. This means that you can just guess a buffer size and increase it if necessary.
但是在我的系统上(Ubuntu 14.04,glibc 2.19),vnprintf在为提供的空间使用太多参数时会导致分段错误.同时,snprintf()
族的语义是否发生了巨大变化?确保您有足够的缓冲区空间的现代方法是什么?
But on my system (Ubuntu 14.04, glibc 2.19) vnprintf causes a segmentation fault when called with too many arguments for the provided space. Did the semantics of the snprintf()
family change that drastically in the meantime? And what is the modern way of ensuring you hand it enough buffer space?
推荐答案
这是在除SunOS 4(已淘汰20年)之外的所有操作系统上使用snprintf
和vsnprintf
的正确方法.问题出在其他地方.
This is the correct way to use snprintf
and vsnprintf
on every operating system except SunOS 4 (which has been obsolete for 20 years), so your problem is somewhere else.
我会做一个纯粹的猜测,我几乎可以确定您的问题是,您要将va_list ap
传递给vsnprintf
并将其消耗掉,然后您希望在下一次将其重置称呼.这是不正确的,并且已在多年前就停止在gcc中工作(因为它仅适用于某些架构).
I'll make a pure guess and say that I'm almost certain that your problem is that you're passing the va_list ap
into vsnprintf
which consumes it and then you expect it to be reset on the next call. This is incorrect and has stopped working in gcc many years ago (because it only worked on certain architectures).
更改:
n = vsnprintf(p, size, fmt, ap);
收件人:
va_list apc;
va_copy(apc, ap);
n = vsnprintf(p, size, fmt, apc);
va_end(apc);
看看是否有帮助.
这是一个简单的测试,以了解发生了什么事情:
Here's a simple test to see what's going on:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
void
foo(const char *fmt, va_list ap)
{
#ifdef BAD
vprintf(fmt, ap);
#else
va_list apc;
va_copy(apc, ap);
vprintf(fmt, apc);
va_end(apc);
#endif
vprintf(fmt, ap);
}
void
bar(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
foo(fmt, ap);
va_end(ap);
}
int
main(int argc, char **argv)
{
bar("foo %s\n", "bar");
return 0;
}
运行时,我得到以下信息:
When run I get this:
$ cc -o foo foo.c && ./foo
foo bar
foo bar
$ cc -DBAD -o foo foo.c && ./foo
foo bar
foo ����
这篇关于您如何安全地调用vsnprintf()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!