Подтвердить что ты не робот

Как подсчитать количество аргументов, переданных функции, которая принимает переменное количество аргументов?

Как подсчитать количество аргументов, переданных функции в следующей программе:

#include<stdio.h>
#include<stdarg.h>
void varfun(int i, ...);
int main(){
        varfun(1, 2, 3, 4, 5, 6);
        return 0;
}
void varfun(int n_args, ...){
        va_list ap;
        int i, t;
        va_start(ap, n_args);
        for(i=0;t = va_arg(ap, int);i++){
               printf("%d", t);
        }
        va_end(ap);
}

Эта программа выводит мой компилятор gcc под ubuntu 10.04:

234561345138032514932134513792

так, как найти, сколько нет. аргументов, фактически переданных функции?

4b9b3361

Ответ 1

Вы не можете. Вы должны управлять вызывающим, чтобы каким-то образом указать количество аргументов. Вы можете:

  • Передайте количество аргументов в качестве первой переменной
  • Требовать, чтобы последний аргумент переменной был нулевым, нулевым или любым
  • Пусть первый аргумент описывает ожидаемое (например, строка формата printf определяет, какие аргументы должны следовать)

Ответ 2

Вы не можете. Что-то еще вам скажет (например, для printf, это подразумевается количеством дескрипторов формата% в строке формата)

Ответ 3

Если у вас есть совместимый с C99 компилятор (включая препроцессор), вы можете обойти эту проблему, объявив макрос, который вычисляет количество аргументов для вас. Для этого достаточно немного, вы можете использовать P99_VA_ARGS из макроса P99 для достижения этого.

Ответ 4

Вы не можете. varargs не предназначены для этого. Вам нужно реализовать другой механизм, чтобы сообщить функции, сколько аргументов существует. Один из распространенных вариантов состоит в том, чтобы передать аргумент дозорного устройства в конце списка параметров, например:

varfun(1, 2, 3, 4, 5, 6, -1);

Другой - передать счет в начале:

varfun(6, 1, 2, 3, 4, 5, 6);

Это чище, но не так безопасно, так как легче получить неверный счет или забыть его обновить, чем запомнить и поддерживать часовую в конце.

Это зависит от вас, как вы это делаете (рассмотрите модель printf, в которой строка формата определяет, сколько - и какой тип - аргументов).

Ответ 5

Вы можете позволить препроцессору помочь вам обмануть эту стратегию, украденную и измененную из другого ответа:

#include <stdio.h>
#include <stdarg.h>

#define PP_NARG(...) \
         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
         _71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
         _81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
         _91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
         _101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
         _111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
         _121,_122,_123,_124,_125,_126,_127,N,...) N
#define PP_RSEQ_N() \
         127,126,125,124,123,122,121,120, \
         119,118,117,116,115,114,113,112,111,110, \
         109,108,107,106,105,104,103,102,101,100, \
         99,98,97,96,95,94,93,92,91,90, \
         89,88,87,86,85,84,83,82,81,80, \
         79,78,77,76,75,74,73,72,71,70, \
         69,68,67,66,65,64,63,62,61,60, \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

void _variad(size_t argc, ...);
#define variad(...) _variad(PP_NARG(__VA_ARGS__), __VA_ARGS__)

void _variad(size_t argc, ...) {
    va_list ap;
    va_start(ap, argc);
    for (int i = 0; i < argc; i++) {
        printf("%d ", va_arg(ap, int));
    }
    printf("\n");
    va_end(ap);
}

int main(int argc, char* argv[]) {
    variad(2, 4, 6, 8, 10);
    return 0;
}

Здесь есть несколько умных трюков.

1) Вместо прямого вызова вариационной функции вы вызываете макрос, который подсчитывает аргументы и передает аргумент count в качестве первого аргумента функции. Конечный результат препроцессора на main выглядит следующим образом:

_variad(5, 2, 4, 6, 8, 10);

2) PP_NARG - умный макрос для подсчета аргументов.

Рабочая лошадка здесь PP_ARG_N. Он возвращает свой 128-й аргумент, игнорируя первые 127 аргументов (называемых произвольно _1 _2 _3 и т.д.), Называя 128-й аргумент N и определяя результат макроса N.

PP_NARG вызывает PP_ARG_N с __VA_ARGS__, объединенным с PP_RSEQ_N, обратную последовательность чисел, отсчитывающую от 127 до 0.

Если вы не укажете аргументы, 128-е значение PP_RSEQ_N равно 0. Если вы передадите один аргумент PP_NARG, то этот аргумент будет передан в PP_ARG_N как _1; _2 будет 127, а 128-й аргумент для PP_ARG_N будет равен 1. Таким образом, каждый аргумент в __VA_ARGS__ накладывает PP_RSEQ_N на один, оставляя правильный ответ в 128-м слоте.

(По-видимому 127 аргументов - максимум C позволяет.)

Ответ 6

Самый безопасный способ описан выше. Но если вам действительно нужно знать количество аргументов без добавления дополнительного аргумента, то вы можете сделать это таким образом (но обратите внимание, что он очень зависит от машины, зависит от ОС и даже в редких случаях зависит от компилятора). Я запустил этот код с помощью Visual Studio 2013 на 64-разрядном DELL E6440.

Другой момент, когда я разделил sizeof (int), это было потому, что все мои аргументы были int. Если у вас разные аргументы размера, там моя потребность в некоторой корректировке.

Это связано с тем, что вызывающая программа использует стандартное соглашение о вызове C. (varfun() получает количество аргументов из "add esp, xxx", и есть две формы формы добавления: (1) и форма (2). Во втором тесте я передал структуру, потому что я хотел симулировать множество аргументов, чтобы заставить длинную форму).

Отпечатанные ответы будут 6 и 501.

    varfun(1, 2, 3, 4, 5, 6);
00A03CC8 6A 06                push        6  
00A03CCA 6A 05                push        5  
00A03CCC 6A 04                push        4  
00A03CCE 6A 03                push        3  
00A03CD0 6A 02                push        2  
00A03CD2 6A 01                push        1  
00A03CD4 E8 E5 D3 FF FF       call        _varfun (0A010BEh)  
00A03CD9 83 C4 18             add         esp,18h  
    varfun(1, x);
00A03CDC 81 EC D0 07 00 00    sub         esp,7D0h  
00A03CE2 B9 F4 01 00 00       mov         ecx,1F4h  
00A03CE7 8D B5 28 F8 FF FF    lea         esi,[x]  
00A03CED 8B FC                mov         edi,esp  
00A03CEF F3 A5                rep movs    dword ptr es:[edi],dword ptr [esi]  
00A03CF1 6A 01                push        1  
00A03CF3 E8 C6 D3 FF FF       call        _varfun (0A010BEh)  
00A03CF8 81 C4 D4 07 00 00    add         esp,7D4h 



#include<stdio.h>
#include<stdarg.h>
void varfun(int i, ...);
int main()
{
    struct eddy
    {
        int x[500];
    } x = { 0 };
    varfun(1, 2, 3, 4, 5, 6);
    varfun(1, x);
    return 0;
}

void varfun(int n_args, ...)
{
    va_list ap;
    unsigned long *p;
    unsigned char *p1;
    unsigned int nargs;
    va_start(ap, n_args);
    p = (long *)(ap - _INTSIZEOF(int) - _INTSIZEOF(&varfun));
    p1 = (char *)*p;
    if (*p1 == 0x83)     // short add sp,x
    {
        nargs = p1[2] / sizeof(int);
    }
    else
    {
        nargs = *(unsigned long *)(p1+2) / sizeof(int);
    }
    printf("%d\n", nargs);
    va_end(ap);
}

Ответ 7

Вы также можете использовать значащее значение, указывающее конец аргументов. Как 0 или -1. Или максимальный размер шрифта, такой как 0xFFFF для ushort.

В противном случае вам нужно упомянуть счетчик upfront или сделать его вычитаемым из другого аргумента (format для printf() как функции).

Ответ 8

В этом коде возможно, когда вы передаете только указатель

# include <unistd.h>
# include <stdarg.h>
# include <string.h>
# include <errno.h>

size_t __print__(char * str1, ...);
# define print(...) __print__(NULL, __VA_ARGS__, NULL)
# define ENDL "\n"

int main() {

  print("1", ENDL, "2", ENDL, "3", ENDL);

  return 0;
}

size_t __print__(char * str1, ...) {
    va_list args;
    va_start(args, str1);
    size_t out_char = 0;
    char * tmp_str;
    while((tmp_str = va_arg(args, char *)) != NULL)
        out_char = out_char + write(1, tmp_str,strlen(tmp_str));
    va_end(args);
    return out_char;
}

Ответ 9

Возможно и просто, просто добавьте еще одну переменную k в цикл и сначала назначьте ее 1, попробуйте этот код

#include <stdio.h>
#include <stdarg.h>

void varfun(int i, ...);

int main(){
        varfun(1,2);
        return 0;
}

void varfun(int n_args, ...)
        {
        va_list ap;
        int i, t, k;
        k = 1;
        va_start(ap, n_args);
        for(i=0;i <= va_arg(ap, int);i++){
               k+=1;
        }
        printf("%d",k);
        va_end(ap);
}