在 C 中没有 va_list 的情况下访问可变参数函数的参数 [英] Access to variadic function' arguments without va_list in C

查看:44
本文介绍了在 C 中没有 va_list 的情况下访问可变参数函数的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以使用指向最后一个命名参数的指针(空指针?)遍历可变参数函数的参数?(我知道这不是使用可变参数的正确方法,但我仍然很感兴趣是否可行)

Is it possible to iterate through variadic function' arguments using pointer (void pointer?) to last named argument? (I know that's not the right way to work with variadic arguments, but I'm still interested if that would work)

设置指向字符串末尾的指针不起作用,因为在我开始移动指针后,它指向程序中使用的其他字符串.

Setting pointer to the end of the string doesn't work, because after I start moving the pointer, it points to other strings used in the program.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void form_date(MON* datePtr, int dayMonth, int dayYear, int month);
MON* evaluate_date(MON* datePtr, int count, int dayArg);
void what_month(char *format, ...);
void output(MON* datePtr, int count);

int main(void)
{ 
what_month("ii", 126, 125);
return 0;
}

void what_month(char *format, ...){

    char* arg_ptr = format+2;
    int* arg_int_ptr;
    double* arg_double_ptr;

    MON dateArr[MAX_DATE];
    int count = 0;
    int dayYear;
    char *ptrFormat = format;

    for(; *ptrFormat != '\0'; ptrFormat++){
        if(*ptrFormat == 'i'){
            arg_int_ptr = (int*) arg_ptr;
            dayYear = *arg_int_ptr;
            arg_int_ptr++;
        }

        if(*ptrFormat == 'd'){
            arg_double_ptr = (double*) arg_ptr;
            dayYear = *arg_double_ptr;
            arg_int_ptr++;
        }
        evaluate_date(dateArr, count, dayYear);
            count++;
     }
    output(dateArr, count);
}


void form_date(MON* datePtr, int dayYear, int dayMonth, int month){
    char month_names[][15] = {"January", "February", "March", "April", "May", "June",
                              "July", "August", "September", "October", "November",
                              "December", "INVALID_MONTH"};

    datePtr->day_of_year = dayYear;
    datePtr->day_of_month = dayMonth;

    if(month == -1){
        strcpy(datePtr->month, month_names[12]);
    }
    else {
        strcpy(datePtr->month, month_names[month]);
    }
}

MON* evaluate_date(MON* dateArr, int count, int dayArg){
    int months_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int j;
    int dayMonth;
    int sumDays = 0;


        if (dayArg > 365 || dayArg < 1){
            form_date(dateArr + count, dayArg, -1,  -1); 
            count++;
        }

        else {
            for(j = 0; j < 12; j++){
                sumDays += months_days[j];
                if (dayArg <= sumDays)
                    break;
            }
            dayMonth = months_days[j] - (sumDays - dayArg);
            sumDays = 0;
            if (dayMonth == 0){
                dayMonth++;
            }

            form_date(dateArr + count, dayArg, dayMonth, j);
        }
        return dateArr;
}

void output(MON* dateArr, int count){
    int i, j;

    for(i = 0; i < 80; i++)
        printf("_");

    printf("\n");
    for(i = 0; i < 80; i++)
        printf("_");

    for(j = 0; j < count; j++){
        if (j % 100 == 0 && j != 0){
            puts("Press any key to continue");
            getchar();
        }

        printf("\n%-7d  :::  %7d, %-8s %5s\n", dateArr[j].day_of_year, dateArr[j].day_of_month,
               dateArr[j].month, "|");
    }
    for(i = 0; i < 80; i++)
        printf("_");
}

推荐答案

首先 - 在 C/C++ 中,字符串不会通过值传递,除非封装在某些结构/类中,因此在堆栈中您会发现指向字符串的指针不是一个字符串本身.

First - strings are not passed via value in C/C++ unless encapsulated in some struct/class so on the stack you'll find a pointer to a string not a string itself.

您不应该使用指针手动处理变量参数列表,因为它首先不可移植.

You should not handle variable argument list manually using pointers as that is not portable in first place.

为什么不便携?以下是一些问题:

Why not portable ? Here are some problems:

  • 不要假设您的代码将在 x86 上执行,其中执行 push 时堆栈指针的行为类似于 *--sp = value
  • 并不是所有的栈(不是在所有的 archs 上)都会在存储值之前增长 - ARM 处理器让你将栈推送实现为 *--sp = val, *++sp = val, --*sp = val, ++*sp = val(由您决定)
  • 什么是一种架构上的 32bit int 可能是另一种(或 64)架构上的 16bit int
  • 即使使用相同的编译器 - 如果您编译为 64 位指令集,您会得到不同的调用约定,这会破坏您的代码

此外,在 C/C++ 的标准调用约定中,您的最后一个参数首先被压入堆栈,因此您不能将它用于可变参数函数(因为您无法立即从您的函数中找到它).cdecl 调用约定以相反的顺序将参数压入堆栈,即:

Moreover in standard calling convention for C/C++ your last argument is pushed on stack first so you cannot use it for variadic functions (as you cannot immediately locate it fromyour func). cdecl calling convension pushes arguments on stack in reversed order ie.:

func(a,b)

push b
push a
call func

这种相反的约定仅用于允许可变参数函数工作,因为您的第一个参数 a(在可变参数中总是需要的)总是可以很容易地从堆栈中访问,因为它的位置是已知的(因为最后推).要获得其他参数(或了解它们的数量或类型),您通常必须解析第一个参数 - 就像在 printf 中一样.

This reversed convention is only for the purpose of allowing variadic functions to work since your first argument a (which is always needed in varargs) can always be easily accessed from stack as it's location is known (because pushed last). To get other arguments (or to understand how many they are, or their types) you usually must parse first argument - just like in printf.

希望这能说明问题.

一些可能有帮助的附加信息:

Some additional info that may help:

这篇关于在 C 中没有 va_list 的情况下访问可变参数函数的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆