C中va_list在32位和64位机器的区别与差异
博客搬家,原地址:https://langzi989.github.io/2018/01/01/C中va_list类型在32位和64位机器的区别与使用/
在将程序从32位机器移植到64位机器的过程中经常出现一些奇奇怪怪的错误,这里记录一下在使用可变参数的过程中导致在32位机器上正常运行的程序移植到64位机器上之后出现段错误的发现过程以及解决方案。
首先看下面一段代码:
#include <iostream>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
void parse(va_list ap) {
char* arg;
arg = va_arg(ap, char*);
std::cout << arg << std::endl
<< strlen(arg) << std::endl;
}
void test(const char* format, ...) {
va_list ap;
va_start(ap, format);
for (int i = 0; i < 2; i++) {
parse(ap);
}
va_end(ap);
}
int main() {
test("hget %s %s", "abc", "123456");
}
在32位机器的运行结果如下:
abc
3
abc
3
在64位机器运行结果如下:
abc
3
123456
6
原因分析
出现上述结果的原因是由于va_list类型在32位和64位机器的类型不同导致的.
32位va_list
在32位上,va_list的定义为:
//注意,由于中间宏过多,这里省去了中间如_VA_LIST宏,直接给出实际定义。
typedef va_list char**;
64位va_list
在64位上va_list定义为一个结构体数组,并且数组中记录了可变参数被读的偏移量:
// Figure 3.34
typedef struct {
unsigned int gp_offset;
unsigned int fp_offset;
void *overflow_arg_area;
void *reg_save_area;
} va_list[1];
程序异常分析
当在32位机器上将va_list(char**)作为参数传递给函数的时候,该函数将从头开始读取该变长参数,还是使用va_list完毕并不记录当前va_list被读的偏移量,所以当第二次传入该va_list还是从头开始读取。
当在64为机器上将va_list(struct 数组)作为参数传递给函数的时候,该函数读取va_list完毕之后,将读取的偏移量记录在结构体中,由于其为数组传入函数,所以该被调用的函数改变了传入的va_list的偏移量。导致下次调用该函数从记录的偏移量开始读,造成不可预测或者内存越界等问题。
移植解决方案
将va_list初始化写到for循环内部,每次调用函数前都初始化va_list即可。
#include <iostream>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
void parse(va_list ap) {
char* arg;
arg = va_arg(ap, char*);
std::cout << arg << std::endl
<< strlen(arg) << std::endl;
}
void test(const char* format, ...) {
for (int i = 0; i < 2; i++) {
va_list ap;
va_start(ap, format);
parse(ap);
va_end(ap);
}
}
int main() {
test("hget %s %s", "abc", "123456");
}
参考:
https://*.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure
http://blog.csdn.net/doubleface999/article/details/55798710
下一篇: 红旗linux中的ftp配置过程