C可变包装器
C variadic wrapper
为了输出格式化的调试输出,我为 vsfprint
编写了一个包装器。现在,我想为输出缓冲区分配足够的内存,而不是仅仅要求一个随机的高缓冲区大小(它是一个小型嵌入式平台(ESP8266))。为此,我遍历变量参数,直到找到 NULL。
如果我没有忘记在每次调用中添加一个 (char *)NULL
参数,这很好用。所以,我想,让我们创建另一个包装器,一个只传递所有参数并添加一个 (char *) NULL
参数的函数:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // malloc
void write_log(const char *format, ...) {
char* buffdyn;
va_list args;
// CALC. MEMORY
size_t len;
char *p;
if(format == NULL)
return;
len = strlen(format);
va_start(args, format);
while((p = va_arg(args, char *)) != NULL)
len += strlen(p);
va_end(args);
// END CALC. MEMORY
// ALLOCATE MEMORY
buffdyn = malloc(len + 1); /* +1 for trailing [=10=] */
if(buffdyn == NULL) {
printf("Not enough memory to process message.");
return;
}
va_start(args, format);
//vsnprintf = Write formatted data from variable argument list to sized buffer
vsnprintf(buffdyn, len, format, args);
va_end(args);
printf("%s\r\n",buffdyn);
free(buffdyn);
}
void write_log_wrapper(const char *format, ...) {
va_list arg;
va_start(arg, format);
write_log(format,arg,(char *)NULL);
va_end(arg);
}
int main()
{
const char* sDeviceName = "TEST123";
const char* sFiller1 = "12345678";
write_log_wrapper("Welcome to %s%s", sDeviceName,sFiller1);
write_log("Welcome to %s%s", sDeviceName,sFiller1, (char *)NULL);
return 0;
}
直接调用write_log()
函数就可以了(如果你没有忘记NULL参数的话)。调用write_log_wrapper()
函数只会显示第一个参数,然后在输出中添加一个“(nu”(垃圾?)。
我做错了什么?这是实现我最初的目标的好方法吗?
谢谢。
What am I doing wrong?
传递一个 va_list arg
write_log(format, arg, (char *)NULL);
不等于传递几个char*
write_log("Welcome to %s%s", sDeviceName, sFiller1, (char *)NULL);
您不会绕过标记传递的参数结束的标记,即 (char*) NULL
或您决定使用的任何内容。
备选方案是
- 显式传递参数的数量,可能作为第二个参数
- 解析转换说明符的格式字符串,实际上是模仿
printf
所做的。
要确定需要多大的缓冲区来保存输出字符串,您需要完全解析整个格式字符串并实际展开参数。
您可以自己做,重复 printf()
及其同类的所有处理并希望不要出错,或者您可以使用 vsnprintf()
- 首先确定大小,然后然后将输入实际扩展为一个输出字符串。
#define FIXED_SIZE 64
void write_log(const char *format, ...)
{
// set up a fixed-size buffer and a pointer to it
char fixedSizeBuffer[ FIXED_SIZE ];
char *outputBuffer = fixedSizeBuffer;
// no dynamic buffer yet
char *dynamicBuffer = NULL;
// get the variable args
va_list args1;
va_start( args1, format );
// need to copy the args even though we won't know if we
// need them until after we use the first set
va_list args2;
va_copy( args2, args1 );
// have to call vsnprintf at least once - might as well use a small
// fixed-size buffer just in case the final string fits in it
int len = vsnprintf( fixedSizeBuffer, sizeof( fixedSizeBuffer ), format, args1 );
va_end( args1 );
// it didn't fit - get a dynamic buffer, expand the string, and
// point the outputBuffer pointer at the dynamic buffer so later
// processing uses the right string
if ( len > sizeof( fixedSizeBuffer ) )
{
dynamicBuffer = malloc( len + 1 );
vsnprintf( dynamicBuffer, len + 1, format, args2 );
outputBuffer = dynamicBuffer;
}
va_end( args2 );
// do something with outputBuffer
free( dynamicBuffer );
return;
}
如果您只想确保所有调用都在最后收到一个 setinel,请使用宏:
#define WRITE_LOG(...) write_log(__VA_ARGS__, (char*)0)
这样可以确保最后总是有一个额外的 0
。
还要小心NULL
。在 C 标准中未指定此解析的表达式。常见的情况是 0
和 (void*)0
。所以在 64 位架构上,它们可能具有不同的宽度(第一个是 32 位,第二个是 64 位)。可变参数函数在这里接收到错误的宽度可能是致命的。因此,我使用了 (char*)0
,这是您的函数似乎期望的类型。 (但 (void*)0
也适用于这种特殊情况。)
为了输出格式化的调试输出,我为 vsfprint
编写了一个包装器。现在,我想为输出缓冲区分配足够的内存,而不是仅仅要求一个随机的高缓冲区大小(它是一个小型嵌入式平台(ESP8266))。为此,我遍历变量参数,直到找到 NULL。
如果我没有忘记在每次调用中添加一个 (char *)NULL
参数,这很好用。所以,我想,让我们创建另一个包装器,一个只传递所有参数并添加一个 (char *) NULL
参数的函数:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // malloc
void write_log(const char *format, ...) {
char* buffdyn;
va_list args;
// CALC. MEMORY
size_t len;
char *p;
if(format == NULL)
return;
len = strlen(format);
va_start(args, format);
while((p = va_arg(args, char *)) != NULL)
len += strlen(p);
va_end(args);
// END CALC. MEMORY
// ALLOCATE MEMORY
buffdyn = malloc(len + 1); /* +1 for trailing [=10=] */
if(buffdyn == NULL) {
printf("Not enough memory to process message.");
return;
}
va_start(args, format);
//vsnprintf = Write formatted data from variable argument list to sized buffer
vsnprintf(buffdyn, len, format, args);
va_end(args);
printf("%s\r\n",buffdyn);
free(buffdyn);
}
void write_log_wrapper(const char *format, ...) {
va_list arg;
va_start(arg, format);
write_log(format,arg,(char *)NULL);
va_end(arg);
}
int main()
{
const char* sDeviceName = "TEST123";
const char* sFiller1 = "12345678";
write_log_wrapper("Welcome to %s%s", sDeviceName,sFiller1);
write_log("Welcome to %s%s", sDeviceName,sFiller1, (char *)NULL);
return 0;
}
直接调用write_log()
函数就可以了(如果你没有忘记NULL参数的话)。调用write_log_wrapper()
函数只会显示第一个参数,然后在输出中添加一个“(nu”(垃圾?)。
我做错了什么?这是实现我最初的目标的好方法吗?
谢谢。
What am I doing wrong?
传递一个 va_list arg
write_log(format, arg, (char *)NULL);
不等于传递几个char*
write_log("Welcome to %s%s", sDeviceName, sFiller1, (char *)NULL);
您不会绕过标记传递的参数结束的标记,即 (char*) NULL
或您决定使用的任何内容。
备选方案是
- 显式传递参数的数量,可能作为第二个参数
- 解析转换说明符的格式字符串,实际上是模仿
printf
所做的。
要确定需要多大的缓冲区来保存输出字符串,您需要完全解析整个格式字符串并实际展开参数。
您可以自己做,重复 printf()
及其同类的所有处理并希望不要出错,或者您可以使用 vsnprintf()
- 首先确定大小,然后然后将输入实际扩展为一个输出字符串。
#define FIXED_SIZE 64
void write_log(const char *format, ...)
{
// set up a fixed-size buffer and a pointer to it
char fixedSizeBuffer[ FIXED_SIZE ];
char *outputBuffer = fixedSizeBuffer;
// no dynamic buffer yet
char *dynamicBuffer = NULL;
// get the variable args
va_list args1;
va_start( args1, format );
// need to copy the args even though we won't know if we
// need them until after we use the first set
va_list args2;
va_copy( args2, args1 );
// have to call vsnprintf at least once - might as well use a small
// fixed-size buffer just in case the final string fits in it
int len = vsnprintf( fixedSizeBuffer, sizeof( fixedSizeBuffer ), format, args1 );
va_end( args1 );
// it didn't fit - get a dynamic buffer, expand the string, and
// point the outputBuffer pointer at the dynamic buffer so later
// processing uses the right string
if ( len > sizeof( fixedSizeBuffer ) )
{
dynamicBuffer = malloc( len + 1 );
vsnprintf( dynamicBuffer, len + 1, format, args2 );
outputBuffer = dynamicBuffer;
}
va_end( args2 );
// do something with outputBuffer
free( dynamicBuffer );
return;
}
如果您只想确保所有调用都在最后收到一个 setinel,请使用宏:
#define WRITE_LOG(...) write_log(__VA_ARGS__, (char*)0)
这样可以确保最后总是有一个额外的 0
。
还要小心NULL
。在 C 标准中未指定此解析的表达式。常见的情况是 0
和 (void*)0
。所以在 64 位架构上,它们可能具有不同的宽度(第一个是 32 位,第二个是 64 位)。可变参数函数在这里接收到错误的宽度可能是致命的。因此,我使用了 (char*)0
,这是您的函数似乎期望的类型。 (但 (void*)0
也适用于这种特殊情况。)