我正在开发AVR平台。 avr-libc不提供asprintf()
。我试图引入我的项目的库需要它。有用的是,同一个库包含一个实现(如下)。不幸的是,它提供了奇怪的结果。具体来说,我已经确定vsnprintf()
的返回码永远不正确;而不是随机的结果,我似乎总是看到连续调用时不正确值(5,1等)的相同进展。
对此功能的调用是:asprintf(&str, "%s[%d]", name, val);
。 str
是调用函数中堆栈的char*
。 name
是一个简单的短文本描述符,我已经验证了它不是null而不是零长度。 val
是一个简单的索引,在调用asprintf()
的循环中递增。此调用构建的结果字符串应为7个字符长(不包括空终止符)。整个图书馆都会调用asprintf()
。我对包含库的使用只在这个单循环中执行它(两次)。如果我删除此调用并将结果替换为虚拟值,则库将按预期完成其工作。在ret
的实现中将缓冲区分配给解除引用的asprintf()
指针时,执行似乎崩溃了。
尽管有大量关于缓冲区,指针解除引用和管理var args的实验,但我无法使此功能正常运行。但是,在OS X上交叉编译它可以正常工作。
我知道vsnprintf()
的行为在所有平台上并不一定相同。也就是说,据我所知,这种用法应该按预期工作。
有什么想法吗?它可能是函数本身之外的东西吗?某种导致问题的stdio初始化或链接库选项?
int asprintf(char **ret, const char *fmt, ...) {
va_list ap1;
va_list ap2;
int count;
va_start(ap1, fmt);
va_copy(ap2, ap1);
count = vsnprintf(NULL, 0, fmt, ap1);
va_end(ap1);
if(count > 0) {
char* buffer;
if (!(buffer = (char*)malloc(count+1))) {
return -1;
}
count = vsnprintf(buffer, count+1, fmt, ap2);
*ret = buffer;
}
va_end(ap2);
return count;
}
答案 0 :(得分:1)
根据以前的评论,事实证明asprintf
在没有范围内可见原型的情况下被调用。在通话之前添加必要的声明解决了问题。
根本原因是C
需要 variadic 函数才能在使用前声明正确的原型。
例如,Are prototypes required for all functions in C89, C90 or C99?提到了这一点。
对可变参数函数(如printf或scanf)的任何调用都必须具有可见原型
comp.lang.c FAQ list - Question 15.1提供了关于“ why ”的一些见解。
问:我听说你必须#include< stdio.h>在调用printf之前。为什么呢?
A:因此,适用于printf的原型将在范围内。
编译器可以对接受可变长度参数列表的函数使用不同的调用序列。 (如果使用可变长度参数列表的调用效率低于使用固定长度的调用,则可能会这样做。)因此,原型(使用省略号符号``...''表示参数列表是变量的每当调用varargs函数时,length必须在范围内,以便编译器知道使用varargs调用机制。
答案 1 :(得分:0)
您是否检查了
的定义va_start
宏?
这可能会导致问题,因为如果没有正确定义,参数的地址可能指向格式指针的“后面”。如果没有问题,那么问题可能出在vsnprintf实现中,而不是。
答案 2 :(得分:0)
stdio不需要标准初始化。如果您的实现需要初始化,那么该信息有望在文档中。
如果您的 vnsprintf 被破坏,您可以使用 vsprintf 。这是一个来自FreeTDS的版本:
int
vasprintf(char **ret, const char *fmt, va_list ap)
{
FILE *fp;
if ((fp = fopen(_PATH_DEVNULL, "w")) == NULL)
return -1;
if ((fp == NULL) && ((fp = fopen(_PATH_DEVNULL, "w")) == NULL))
return -1;
len = vfprintf(fp, fmt, ap);
if (fclose(fp) != 0)
return -1;
if (len < 0)
return len;
if ((buf = malloc(len + 1)) == NULL) {
errno = ENOMEM;
return -1;
}
if (vsprintf(buf, fmt, ap) != len)
return -1;
*ret = buf;
return len;
}
根据您的需要,您可以重新使用文件描述符。
答案 3 :(得分:0)
我今天遇到了这个问题,正如dxiv回答的那样,关键是可见原型。但是我想扩展一下:不仅仅是可变参数不能正常工作。在我的例子中,构建了项目并调用了函数,但没有一个参数正常工作。这是一个非常简单的示例。 (函数uprintf()是一个自定义函数,用于通过UART打印输出。)
spy <- read.csv("http://ichart.finance.yahoo.com/table.csv?s=SPY")
从main()调用原型隐藏:
void log_console( const char * fmtstring,... )
{
uprintf("Start of log_console\n");
uprintf(fmtstring);
}
在这种情况下,输出为:
//void log_console( char const * fmtstring,... ); log_console("Test message to console =======\n"); uprintf("After test message to console\n");
这表示正在调用该函数,但未正确定义fmtstring。但原型可见:
Start of log_console
After test message to console
该函数现在可以访问传入参数,并且写入UART的字符串符合预期:
void log_console( char const * fmtstring,... ); log_console("Test message to console =======\n"); uprintf("After test message to console\n");
一旦原型到位,这个简单的演示示例就可以了,我的完整实际函数也是如此,它使用了(...)参数。