programing

변수 개수의 인수를 받아들이는 함수에 전달된 인수 수를 계산하는 방법은 무엇입니까?

prostudy 2022. 6. 12. 11:56
반응형

변수 개수의 인수를 받아들이는 함수에 전달된 인수 수를 계산하는 방법은 무엇입니까?

다음 프로그램에서 함수에 전달된 인수 수를 계산하는 방법:

#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);
}

이 프로그램은 ubuntu 10.04의 gcc 컴파일러에서 출력됩니다.

234561345138032514932134513792

그러면 함수에 실제로 전달된 인수의 수를 어떻게 찾을 수 있을까요?

프리프로세서가 이 전략을 사용하여 부정행위를 할 수 있도록 할 수 있습니다.이 전략은 다른 답변에서 도용 및 조정된 것입니다.

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

#define PP_NARG(...) \
         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_128TH_ARG(__VA_ARGS__)
#define PP_128TH_ARG( \
          _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) 변수 함수를 직접 호출하는 대신 인수를 카운트하고 인수 카운트를 함수에 첫 번째 인수로 전달하는 매크로를 호출합니다.메인 프리프로세서의 최종 결과는 다음과 같습니다.

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

2)2)PP_NARG있나 영리한 매크로 인수 가치가 있을 거야A인수를 세는영리한 매크로입니다.

그 열심히 일하는 사람 여기여기서일하는은 말은PP_128TH_ARG. 무시함으로써 128번째인수를 반환합니다(독단으로 처음 127개의 인수(임의로 명명됨의 이름)를 첫번째 127주장 묵살함으로써 128를 반환하다._1 _2 _3기타=는 128논쟁의 이름 128번째 인수의이름을 지정합니다.N, 매크로의 결과 규정하는데,및매크로의 결과를 정의한다.N.

PP_NARGinvokes 기동하다PP_128TH_ARGA함께와__VA_ARGS__A연결된과 연결되어 있다.PP_RSEQ_N, 숫자의 쳐진 시퀀스 127이 0부터 거꾸로 세세요, 127부터0까지의 역순서입니다.

만약 여러분이 논쟁을 제공한다, 인수를 지정하지 않을의 128값 경우 128번째 값은.PP_RSEQ_N만약 당신이 1개의 인수를전달하면 0입니다에 인수를 전달하는 0입니다.PP_NARG, 그때 그 주장 그인수는 에 전달됩니다 그러면에게 전달될 것이다.PP_128TH_ARG~하듯이로_1;_2는 127 "128" 입니다.PP_128TH_ARG1면 . 의 각__VA_ARGS__PP_RSEQ_N128번째 을 남겨두고 슬롯에 정답을 남깁니다.

(C가 허용하는 최대 인수는 127인 것 같습니다).

그럴수는 없어요.어떻게든 인수의 수를 나타내려면 , 발신자가 관리할 필요가 있습니다.다음과 같은 작업을 수행할 수 있습니다.

  • 인수 수를 첫 번째 변수로 전달
  • 마지막 변수 인수는 null, 0 또는 기타여야 합니다.
  • 첫 번째 인수에 예상되는 내용을 기술하도록 합니다(예를 들어 printf 형식의 문자열은 다음 인수를 지시합니다).

C99 준거 컴파일러(프리프로세서 포함)가 있는 경우 인수 수를 계산하는 매크로를 선언함으로써 이 문제를 회피할 수 있습니다. 하는 , 해도 .P99_VA_ARGSP99 매크로 패키지에서 이 작업을 수행합니다.

그럴수는 없어요.다른 정보가 필요합니다(예를 들어 printf의 경우 형식 문자열의 % 형식 기술자 수에 의해 암시됩니다).

안 돼, 바라그는 이걸 가능하게 만들도록 설계되지 않았어인수의 수를 함수에 통지하려면 다른 메커니즘을 구현해야 합니다.일반적으로 파라미터 목록 끝에 sentinel 인수를 전달하는 방법이 있습니다.예를 들어 다음과 같습니다.

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

다른 하나는 처음에 카운트를 통과시키는 것입니다.

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

이것은 카운트를 잘못 계산하거나 업데이트를 잊어버리기 쉽기 때문에 마지막에 감시원을 기억하고 유지하는 것보다 더 깨끗하지만 안전하지는 않습니다.

실행 방법은 사용자에게 달려 있습니다(형식 문자열이 인수의 수와 유형을 결정하는 printf 모델을 고려합니다).

가장 안전한 방법은 위와 같습니다.단, 굳이 추가 인수를 추가하지 않고 인수의 수를 알 필요가 있는 경우에는 다음과 같이 할 수 있습니다(단, 이 인수는 머신에 의존하거나 OS에 의존하거나 드물게 컴파일러에 의존하기도 합니다).64비트 DELL E6440에서 Visual Studio 2013을 사용하여 이 코드를 실행했습니다.

또 다른 점은, 내가 크기(int)로 나눈 지점에서, 그것은 나의 모든 주장이 int의 것이었기 때문이다.만약 당신이 다른 크기의 주장을 한다면, 거기에 약간의 조정이 필요합니다.

이것은 표준 C 호출 규약을 사용하기 위한 호출 프로그램에 의존합니다.(varfun()은 "add esp,xx"에서 인수 수를 가져옵니다.add에는 (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);
}

마지막에 또는 NULL을 추가하면 인수를 몇 개라도 가질 수 있어 스택 밖으로 나갈 염려가 없어집니다.

#include <cstdarg>
template<typename _Ty>
inline void variadic_fun1(_Ty param1,...)
{
    va_list arg1;
    //TO_DO

    va_end(arg1);
}
template<typename _Ty> 
void variadic_fun2(_Ty param1,...)
{
    va_list arg1;
    va_start(arg1, param1);
    variadic_fun1(param1, arg1, 0);
    va_end(arg1);
}

EBP에서 포인터에 대한 포인터를 읽습니다.

#define getReturnAddresses() void ** puEBP = NULL; __asm { mov puEBP, ebp };

사용.

getReturnAddresses();
int argumentCount = *((unsigned char*)puEBP[1] + 2) / sizeof(void*) ;
printf("CalledFrom: 0x%08X Argument Count: %i\n", puEBP[1], argumentCount);

portable은 아니지만 __cdecl 메서드의 x86 C++ 우회 경로에서 사용했으며, 변수 개수의 인수가 성공했습니다.

스택/인수에 따라 -1 부분을 조정해야 할 수 있습니다.

나는 이 방법을 생각해내지 못했다.언젠가 UC 포럼에서 찾은 것 같습니다.

이것을 propper 코드로 사용하는 것은 추천할 수 없습니다만, x86 exe에서 __cdecl 호출 규약을 1개의 인수로 사용하고, 나머지가 변수 인수로 되어 있는 경우, 동작합니다. (Win32)

우회 메서드의 호출 규칙 예시.

void __cdecl hook_ofSomeKind_va_list(void* self, unsigned char first, ...)

실증: 우회로를 적용한 타깃 프로세스의 x32dbg 옆에 콘솔 출력이 표시되는 스크린샷

보다 간단하고 우아한 방법은 std::initializer_list를 사용하는 것입니다.또한 간단한 매크로를 사용하여 여분의 괄호를 삭제할 수 있습니다.다음 예를 참조해 주세요.특히 루프의 경우 통상적인 뒤에 있는elts.size() 콜을 참조해 주세요.

void doList(const std::initializer_list<std::string>& elts) {
    int count = 0;
    for (std::string s : elts) {
        cout << "(" << count++ << ") " << s << endl;
    }
    cout << "total: " << count << " also as: " << elts.size() <<endl;
}
// and to get rid of extra brackets:
#define DOLIST(...) doList({ __VA_ARGS__ })

용도:

doList({ "this", "is", "a", "initializer_list", "example" } );
DOLIST("and", "a", "variant", "without", "brackets");

인수의 끝을 나타내는 의미 있는 값을 사용할 수도 있습니다.0이나 -1처럼요또는 0xFFFF와 같은 최대 타입 사이즈는ushort.

그렇지 않으면 카운트를 미리 언급하거나 다른 인수(format동일한 함수)에서 차감할 수 있도록 해야 합니다.

이 코드에서는 포인터만 전달하면 가능합니다.

# 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;
}

템플릿과 이름을 팩에 추가해야 할 수도 있지만 이 경우 3이 인쇄됩니다.

lista.insertar(0, 4, 'W', 4.7);

template<typename... data>
int Lista::insertar(int pos, data... datos) {
    std::cout<< sizeof...(datos)<<std::endl;
}

언급URL : https://stackoverflow.com/questions/4421681/how-to-count-the-number-of-arguments-passed-to-a-function-that-accepts-a-variabl

반응형