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 는 첫 번 째 매개 변수의 메모리 주소 입 니 다 메모리 주소 계산 방법
먼저 위조 코드 를 보 세 요.이 위조 코드 는 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)。
세 번 째 점 을 검증 하 는 코드 를 드 립 니 다.
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 언어 가 변 적 인 매개 변수 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
c 언어 간단한 파일 r/w 조작 방법데이터의 입력과 출력은 거의 모든 C 언어 프로그램과 수반된다. 입력이란 원본에서 데이터를 얻는 것이다. 출력은 단말기에 데이터를 쓰는 것으로 이해할 수 있다.이곳의 원본은 키보드, 마우스, 하드디스크, 시디, 스캐...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.