linux 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");
}

总结:

1.每次函数调用了 va_list 变量 ap后, va_list内部指针发生改变, 此时想取以前的值,必须 va_end后重新va_start 到想要的值,不能自己直接操控指针,必须用对应的函数宏。

2.以下函数都会影响 ap 指针

int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

此问题在32 64上都存在,虽然有时候感觉指针没有发生变化,但还是应该正规使用它


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值