作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我使用vsprintf
的问题是我不能直接获取输入参数,我必须先一个一个地获取输入并将它们保存在void**
中,然后通过这个void**
到vsprintf()
,在windows下是没问题的,但是到了64位linux,gcc编译不了,因为不允许从void**
到 va_list
,有没有人可以给我一些帮助,我应该如何在 linux 下执行此操作?
我可以在 GCC 中动态创建 va_list 吗?
void getInputArgs(char* str, char* format, ...)
{
va_list args;
va_start(args, format);
vsprintf(str, format, args);
va_end(args);
}
void process(void)
{
char s[256];
double tempValue;
char * tempString = NULL;
void ** args_ptr = NULL;
ArgFormatType format; //defined in the lib I used in the code
int numOfArgs = GetNumInputArgs(); // library func used in my code
if(numOfArgs>1)
{
args_ptr = (void**) malloc(sizeof(char)*(numOfArgs-1));
for(i=2; i<numOfArgs; i++)
{
format = GetArgType(); //library funcs
switch(format)
{
case ArgType_double:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
args_ptr[i-2] = (void*) (int)tempValue;
break;
case ArgType_char:
args_ptr[i-2]=NULL;
AllocInCharArg(i, TRUE, &tempString); //lib func
args_ptr[i-2]= tempString;
break;
}
}
}
getInputArgs(s, formatString, (va_list) args_ptr); //Here
// is the location where gcc cannot compile,
// Can I and how if I can create a va_list myself?
}
最佳答案
有一种方法可以做到这一点,但它特定于 Linux 上的gcc
。它确实适用于 Linux(已测试)32 位和 64 位版本。
免责声明:我不赞成使用此代码。它不可移植,它很老套,坦率地说,它是一头走钢丝的不稳定平衡的大象。我只是在证明可以使用 gcc
动态创建 va_list
,这正是最初的问题。
话虽如此,以下文章详细介绍了 va_list
如何与 amd64 ABI 一起工作:Amd64 and Va_arg .
了解va_list
结构的内部结构后,我们可以欺骗va_arg
宏从我们自己构造的va_list
中读取数据:
#if (defined( __linux__) && defined(__x86_64__))
// AMD64 byte-aligns elements to 8 bytes
#define VLIST_CHUNK_SIZE 8
#else
#define VLIST_CHUNK_SIZE 4
#define _va_list_ptr _va_list
#endif
typedef struct {
va_list _va_list;
#if (defined( __linux__) && defined(__x86_64__))
void* _va_list_ptr;
#endif
} my_va_list;
void my_va_start(my_va_list* args, void* arg_list)
{
#if (defined(__linux__) && defined(__x86_64__))
/* va_args will read from the overflow area if the gp_offset
is greater than or equal to 48 (6 gp registers * 8 bytes/register)
and the fp_offset is greater than or equal to 304 (gp_offset +
16 fp registers * 16 bytes/register) */
args->_va_list[0].gp_offset = 48;
args->_va_list[0].fp_offset = 304;
args->_va_list[0].reg_save_area = NULL;
args->_va_list[0].overflow_arg_area = arg_list;
#endif
args->_va_list_ptr = arg_list;
}
void my_va_end(my_va_list* args)
{
free(args->_va_list_ptr);
}
typedef struct {
ArgFormatType type; // OP defined this enum for format
union {
int i;
// OTHER TYPES HERE
void* p;
} data;
} va_data;
现在,我们可以生成 va_list
指针(对于 64 位和 32 位构建都是相同的)使用类似于您的 process()
方法或以下方法:
void* create_arg_pointer(va_data* arguments, unsigned int num_args) {
int i, arg_list_size = 0;
void* arg_list = NULL;
for (i=0; i < num_args; ++i)
{
unsigned int native_data_size, padded_size;
void *native_data, *vdata;
switch(arguments[i].type)
{
case ArgType_int:
native_data = &(arguments[i].data.i);
native_data_size = sizeof(arguments[i]->data.i);
break;
// OTHER TYPES HERE
case ArgType_string:
native_data = &(arguments[i].data.p);
native_data_size = sizeof(arguments[i]->data.p);
break;
default:
// error handling
continue;
}
// if needed, pad the size we will use for the argument in the va_list
for (padded_size = native_data_size; 0 != padded_size % VLIST_CHUNK_SIZE; padded_size++);
// reallocate more memory for the additional argument
arg_list = (char*)realloc(arg_list, arg_list_size + padded_size);
// save a pointer to the beginning of the free space for this argument
vdata = &(((char *)(arg_list))[arg_list_size]);
// increment the amount of allocated space (to provide the correct offset and size for next time)
arg_list_size += padded_size;
// set full padded length to 0 and copy the actual data into the location
memset(vdata, 0, padded_size);
memcpy(vdata, native_data, native_data_size);
}
return arg_list;
}
最后,我们可以使用它:
va_data data_args[2];
data_args[0].type = ArgType_int;
data_args[0].data.i = 42;
data_args[1].type = ArgType_string;
data_args[1].data.p = "hello world";
my_va_list args;
my_va_start(&args, create_arg_pointer(data_args, 2));
vprintf("format string %d %s", args._va_list);
my_va_end(&args);
就是这样。它大部分的工作方式与普通的 va_start
和 va_end
宏相同,但允许您传递自己动态生成的字节对齐指针以供使用而不是依赖调用约定来设置堆栈框架。
关于在 GCC 中动态创建 va_list - 可以做到吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11695237/
我是一名优秀的程序员,十分优秀!