변수 개수의 인수를 받아들이는 함수에 전달된 인수 수를 계산하는 방법은 무엇입니까?
다음 프로그램에서 함수에 전달된 인수 수를 계산하는 방법:
#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_NARG
invokes 기동하다PP_128TH_ARG
A함께와__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_ARG
1면 . 의 각__VA_ARGS__
PP_RSEQ_N
128번째 을 남겨두고 슬롯에 정답을 남깁니다.
(C가 허용하는 최대 인수는 127인 것 같습니다).
그럴수는 없어요.어떻게든 인수의 수를 나타내려면 , 발신자가 관리할 필요가 있습니다.다음과 같은 작업을 수행할 수 있습니다.
- 인수 수를 첫 번째 변수로 전달
- 마지막 변수 인수는 null, 0 또는 기타여야 합니다.
- 첫 번째 인수에 예상되는 내용을 기술하도록 합니다(예를 들어 printf 형식의 문자열은 다음 인수를 지시합니다).
C99 준거 컴파일러(프리프로세서 포함)가 있는 경우 인수 수를 계산하는 매크로를 선언함으로써 이 문제를 회피할 수 있습니다. 하는 , 해도 .P99_VA_ARGS
P99 매크로 패키지에서 이 작업을 수행합니다.
그럴수는 없어요.다른 정보가 필요합니다(예를 들어 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
'programing' 카테고리의 다른 글
밀리초를 "hh:mm:ss" 형식으로 변환하는 방법 (0) | 2022.06.12 |
---|---|
Vue.js - vue-axios 응답 데이터를 저장 및 표시하려고 할 때 공리가 정의되지 않음 (0) | 2022.06.12 |
C의 파일 크기는 어떻게 결정합니까? (0) | 2022.06.12 |
NuxtJs 스토어에서 localStorage에 액세스하려면 어떻게 해야 합니까? (0) | 2022.06.12 |
C11에서의 _Generic 구문 및 사용 예 (0) | 2022.06.12 |