C 언어 는 어떻게 가 변 적 인 매개 변 수 를 실현 하 는 지 상세 하 게 설명 합 니 다.

가 변 매개 변수
가 변 매개 변 수 는 함수 의 매개 변수의 데이터 형식 과 수량 이 모두 고정 되 지 않 은 것 을 말한다.
printf 함수 의 매개 변 수 는 가 변 적 입 니 다.이 함수 의 원형 은 int printf(const char*format,...)입 니 다.
printf 의 용법 을 코드 로 보 여 줍 니 다.

// code-A
#include <stdio.h>
int main(int argc, char **argv)
{
   printf("a is %d, str is %s, c is %c
", 23, "Hello, World;", 'A'); printf("T is %d
", 78); return 0; }
code-A 에서 첫 번 째 printf 문 구 는 4 개의 매개 변수 가 있 고 두 번 째 printf 문 구 는 2 개의 매개 변수 가 있 습 니 다.분명히 printf 의 매개 변 수 는 가 변 적 이다.
이루어지다
코드
code-A
먼저 두 단락 의 코드 를 보 세 요.각각 code-A 와 code-B 입 니 다.

// file stack-demo.c

#include <stdio.h>

// int f(char *fmt, int a, char *str);
int f(char *fmt, ...);
int f2(char *fmt, void *next_arg);
int main(int argc, char *argv)
{
        char fmt[20] = "hello, world!";
        int a = 10;
        char str[10] = "hi";
        f(fmt, a, str);
        return 0;
}

// int f(char *fmt, int a, char *str)
int f(char *fmt, ...)
{
        char c = *fmt;
        void *next_arg = (void *)((char *)&fmt + 4);
        f2(fmt, next_arg);
        return 0;
}


int f2(char *fmt, void *next_arg)
{
        printf(fmt);
        printf("a is %d
", *((int *)next_arg)); printf("str is %s
", *((char **)(next_arg + 4))); return 0; }
컴 파일 실행 결 과 는 다음 과 같 습 니 다.
\#컴 파일
[root@localhost c]# gcc -o stack-demo stack-demo.c -g -m32
\#어 셈 블 리 를 반대 하고 어 셈 블 리 코드 를 dis-stack.asm 에 기록 합 니 다.
[root@localhost c]# objdump -d stack-demo>dis-stack.asm
[root@localhost c]# ./stack-demo
hello, world!a is 10
str is hi
code-B

// file stack-demo.c

#include <stdio.h>

// int f(char *fmt, int a, char *str);
int f(char *fmt, ...);
int f2(char *fmt, void *next_arg);
int main(int argc, char *argv)
{
        char fmt[20] = "hello, world!";
        int a = 10;
        char str[10] = "hi";
     char str2[10] = "hello";
        f(fmt, a, str, str2);
        return 0;
}

// int f(char *fmt, int a, char *str)
int f(char *fmt, ...)
{
        char c = *fmt;
        void *next_arg = (void *)((char *)&fmt + 4);
        f2(fmt, next_arg);
        return 0;
}


int f2(char *fmt, void *next_arg)
{
        printf(fmt);
        printf("a is %d
", *((int *)next_arg)); printf("str is %s
", *((char **)(next_arg + 4))); printf("str2 is %s
", *((char **)(next_arg + 8))); return 0; }
컴 파일 실행 결 과 는 다음 과 같 습 니 다.
\#컴 파일
[root@localhost c]# gcc -o stack-demo stack-demo.c -g -m32
\#어 셈 블 리 를 반대 하고 어 셈 블 리 코드 를 dis-stack.asm 에 기록 합 니 다.
[root@localhost c]# objdump -d stack-demo>dis-stack.asm
[root@localhost c]# ./stack-demo
hello, world!a is 10
str is hi
str2 is hello
분석 하 다.
code-A 에서 f 를 호출 하 는 문 구 는 f(fmt,a,str)입 니 다.code-B 에서 f 를 호출 하 는 문 구 는 f(fmt,a,str,str 2)입 니 다.
int f(char*fmt,...)를 쉽게 알 수 있 습 니 다.매개 변수 가 변 적 인 함수 입 니 다.
키워드
가 변 매개 변 수 를 실현 하 는 관건 적 인 문 구 는:

char c = *fmt;
void *next_arg = (void *)((char *)&fmt + 4);
printf("a is %d
", *((int *)next_arg)); printf("str is %s
", *((char **)(next_arg + 4))); printf("str2 is %s
", *((char **)(next_arg + 8)));
4.567917.&fmt 는 첫 번 째 매개 변수의 메모리 주소 입 니 다
  • next_arg 는 두 번 째 매개 변수의 메모리 주소 입 니 다
  • next_arg+4、next_arg+8 은 각각 세 번 째,네 번 째 매개 변수의 메모리 주소 입 니 다.
  • 왜?
    메모리 주소 계산 방법
    먼저 위조 코드 를 보 세 요.이 위조 코드 는 f 함수 에 대응 하 는 어 셈 블 리 코드 입 니 다.f 에 세 개의 인자 가 있다 고 가정 합 니 다.물론 f 도 네 개의 인자 나 두 개의 인자 가 있 을 수 있다.우 리 는 세 개의 매개 변수의 상황 으로 f 를 관찰 합 시다.
    f:
     ; 입고 ebp
     ; ebp 를 esp 로 설정 합 니 다.
     
     ; ebp+0 은 eip 를 저장 하고 callf 에서 스 택 에 들 어 갑 니 다.
     ; ebp+4 는 오래된 ebp 를 저장 합 니 다.
     ; 첫 번 째 매개 변 수 는 ebp+8 입 니 다.
     ; 두 번 째 인 자 는 ebp+12 입 니 다.
     ; 세 번 째 매개 변 수 는 ebp+16 입 니 다.
     
     ; 함수 f 의 논리
     
     ; 출고 ebp.ebp 는 함수 에 들 어가 기 전의 오래된 ebp 로 회복 되 었 습 니 다.
     ; ret
    f 를 호출 하 는 위조 코드 는:
    ; 창고 에 들 어 가 는 세 번 째 매개 변수
    ; 스 택 에 들 어 가 는 두 번 째 매개 변수
    ; 스 택 첫 번 째 인자
    ; f 호출,eip 저장
    어 셈 블 리 코드 에서 첫 번 째 매개 변수의 메모리 주 소 는 쉽게 확정 되 고 두 번 째,세 번 째,그리고 세 번 째 매개 변수의 메모리 주소 도 쉽게 확정 된다.ebp 를 바탕 으로 특정 길 이 를 늘 릴 수 는 없습니다.
    그러나 우 리 는 첫 번 째 매개 변수 가 반드시 존재 한 다 는 것 만 확인 할 수 있 을 뿐 존재 하 는 두 번 째,세 번 째 와 N 번 째 매개 변수 가 있 는 지 확인 할 수 없습니다.존재 하지 않 을 수 있 는 매개 변 수 를 참고 물 로 사용 하고 다른 매개 변수의 주 소 를 계산 할 이유 가 없다.
    첫 번 째 매개 변 수 는 반드시 존재 하기 때문에 우 리 는 그것 을 다른 매개 변수의 메모리 주 소 를 확인 하 는 참고 물 로 사용 합 니 다.
    메모리 주소
    f 함수 의 C 코드 에서&fmt 는 첫 번 째 매개 변수 가 사용 하 는 f 스 택 요소 의 메모리 주소 입 니 다.다시 말 하면 부분 변수의 메모리 주소 입 니 다.
    부분 변수의 메모리 주 소 는 함수 의 반환 값 으로 할 수 없 지만 이 함수 가 실행 되 기 전에 사용 할 수 있 습 니 다.이 함수 가 호출 한 다른 함수 에서 사용 할 수 있 습 니 다.이것 이 바로 f2 에서 fmt 로 계 산 된 메모리 주 소 를 사용 할 수 있 는 이유 입 니 다.
    난점
    인자 가 int 형식 일 때 인자 의 값 을 가 져 오 는 데 사용*(int*)(nextarg)。
    매개 변수 가 char str[20]일 때 매개 변수의 값 을 가 져 오 는 데 사용*(char**)(nextarg + 4)。
    왜 next 를 직접 사용 하지 않 습 니까?arg、(next_arg+4)는?
    분석*(int*)(nextarg)。
    32 비트 운영 체제 에서 모든 메모리 주소 의 값 은 32 비트 의 정수 로 보인다.그러나 이 정수 값 의 유형 은 unsigned int 가 아니 라 int*입 니 다.
    이 점 에 대해 서 는 gdb 에서 ptype 을 사용 하여 확인 할 수 있 습 니 다.예 를 들 어 작은 코드 int*a 가 있 습 니 다.*a = 5;,ptype a 를 실행 하면 결 과 는 int*입 니 다.
    next_arg 는 정수 일 뿐 데이터 형식 을 잃 었 습 니 다.데이터 형식 을 보충 해 야 합 니 다.우 리 는 이 조작 을'강제 유형 전환'으로 이해 할 수 있다.
    에 대해 서*(int*)(nextarg)앞의*는 이해 하기 쉽 고 포인터 가 가리 키 는 메모리 의 값 을 가 져 옵 니 다.
    일반적인 방식 으로 분석*(char**)(nextarg+4)。
  • 세 번 째 매개 변수 이기 때문에 nextarg+4。
  • 세 번 째 매개 변수의 데이터 형식 은 char str[20]이기 때문이다.경험 에 따 르 면 char str[20]에 대응 하 는 지침 은 char*입 니 다다음 때문에arg+4 는 함수 스 택 요소 의 메모리 주소 일 뿐 대상 요소 에 저 장 된 포인터 입 니 다.그 러 니까 nextarg+4 는 더 블 포인터 형식의 지침 입 니 다.마지막 으로 문자열 을 가리 키 며 경험 에 따라 nextarg+4 의 데이터 형식 은 char*입 니 다.너무 고민 할 필요 없어.간단 한 문자열 을 가리 키 는 두 바늘 을 쓰 고 gdb 의 ptype 을 사용 하여 이러한 유형의 데이터 형식 을 보면 이 점 을 검증 할 수 있 습 니 다4.567917.맨 앞의*,포인터 가 가리 키 는 데 이 터 를 가 져 옵 니 다.
    세 번 째 점 을 검증 하 는 코드 를 드 립 니 다.
    
    char str[20] = "hello";
    char *ptr = str;
    //   gdb ptype    ptype &ptr
    
    인쇄 결 과 는 다음 과 같 습 니 다.
    Breakpoint 1, main (argc=1, argv=0xffffd3f4) at point.c:13
    13  char str7[20] = "hello";
    (gdb) s
    14  char *ptr = str7;
    (gdb) s
    19  int b = 7;
    (gdb) p &str
    $1 = (char **) 0xffffd2fc
    최적화 하 다.
    code-A 와 code-B 에서 우 리 는 인공 적 으로 매개 변수의 유형 에 따라 매개 변 수 를 얻 고*(int*)(next 를 사용 합 니 다.arg)또는*(char**)(nextarg + 4)。
    라 이브 러 리 함수 printf 는 분명히 인공 식별 매개 변수의 유형 이 아니다.
    이 함수 의 첫 번 째 매개 변 수 는%d,%x,%s 등 자리 표시 자 를 포함 합 니 다.첫 번 째 인 자 를 옮 겨 다 니 며%d 를 식별 하면*(int*)nextarg 교체%d.식별 해 내다
    %s,*(char**)nextarg。
    자리 표시 자 를 식별 하고 자리 표시 자 에 따라 포인터 유형 을 선택 하 는 기능 을 실현 하면 완성 도가 높 은 가 변 적 인 파 라 메 터 를 실현 할 수 있 습 니 다.
    총결산
    C 언어 가 가 변 적 인 파 라 메 터 를 어떻게 실현 하 는 지 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 C 언어 가 변 적 인 매개 변수 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기