가변 인수는 gcc에서 어떻게 구현됩니까?
int max(int n, ...)
사용중입니다cdecl
호출자가 복귀한 후에 호출자가 변수를 정리하는 호출 규칙.
매크로가 어떻게 작동하는지 알고 싶습니다.va_end
,va_start
그리고.va_arg
일?
호출자가 인수 배열의 주소를 max로 두 번째 인수로 전달합니까?
C 언어가 스택에 매개 변수를 저장하는 방식을 살펴보면 매크로가 작동하는 방식이 명확해야 합니다:-
Higher memory address Last parameter
Penultimate parameter
....
Second parameter
Lower memory address First parameter
StackPointer -> Return address
(참고로 하드웨어에 따라 스택 포인터는 한 줄 아래로 이동할 수 있으며 상위와 하위가 바뀔 수 있습니다.)
논쟁들은 항상1 이렇게 저장됩니다, 심지어 그것들이 없어도....
매개 변수 유형.
va_start
macro는 첫 번째 함수 파라미터에 대한 포인터를 설정합니다. 예:-
void func (int a, ...)
{
// va_start
char *p = (char *) &a + sizeof a;
}
어떤것은p
두번째 파라미터를 가리킵니다. 더va_arg
매크로가 이 작업을 수행합니다:-
void func (int a, ...)
{
// va_start
char *p = (char *) &a + sizeof a;
// va_arg
int i1 = *((int *)p);
p += sizeof (int);
// va_arg
int i2 = *((int *)p);
p += sizeof (int);
// va_arg
long i2 = *((long *)p);
p += sizeof (long);
}
va_end
macro는 그냥 설정합니다.p
치에 입니다.NULL
.
주의:
- 컴파일러와 일부 RISC CPU를 최적화하면 스택을 사용하지 않고 레지스터에 파라미터를 저장합니다.재의 .
...
매개 변수는 이 기능을 끄고 컴파일러가 스택을 사용하도록 합니다.
스택에 인수가 전달되면,va_
함수들(대부분 매크로로 구현됨)은 단순히 개인 스택 포인터를 조작합니다.이 개인 스택 포인터는 전달된 인수에서 다음으로 저장됩니다.va_start
,그리고 나서.va_arg
매개변수를 반복할 때 "스택"에서 인수를 "pops"합니다.
당신이 그 함수를 부른다고 가정해 보겠습니다.max
다음과 같은 세 가지 매개 변수를 사용합니다.
max(a, b, c);
인사이드 더max
function, 스택은 기본적으로 다음과 같습니다.
+-----+| c || b || a ||| 레트 |SP -> +---------+
SP
진짜 스택 포인터인데, 사실은 아닙니다.a
,b
그리고.c
그들의 가치를 제외하고는 스택에 있는 것.ret
는 반환 주소로, 함수가 완료되었을 때 점프할 위치입니다.
.va_start(ap, n)
does는 인수의 주소를 사용합니다 (n
함수 프로토타입)에서 다음 인수의 위치를 계산하므로 새로운 개인 스택 포인터를 얻을 수 있습니다.
+-----+| c |ap -> | b || a ||| 레트 |SP -> +---------+
사용할때va_arg(ap, int)
Private Stack 포인터가 가리키는 것을 반환하고 다음 인수에서 Private Stack 포인터를 now point로 변경하여 "pops"합니다.이제 스택은 다음과 같습니다.
+-----+ap -> | c || b || a ||| 레트 |SP -> +---------+
이 설명은 물론 단순화된 것이지만 원리를 보여줍니다.
일반적으로 함수 프로토타입이 (,...)로 선언되면 컴파일러는 varargs 플래그로 표시된 구문 트리를 설정하고 명명된 인수의 유형을 참조합니다.엄격한 C 적합성을 위해 각 명명된 인수는 해당 매개 변수가 va_start의 명명 필드이고 va_arg()로 반환될 가능성이 있을 때 va_list를 설정하는 데 필요한 추가 정보를 가져와야 하지만 대부분의 컴파일러는 마지막 명명된 인수에 대해 이 정보를 생성합니다.함수가 정의되면 해당 프롤로그 생성기는 varargs 플래그가 설정되었음을 확인하고 va_start 매크로가 참조할 수 있는 오프셋을 알고 있는 숨겨진 필드를 설정하는 데 필요한 코드를 프레임에 추가합니다.
해당 함수에 대한 참조를 찾으면 이름이 지정된 인수에 대해 va_start 및 va_arg에 대해 설정된 필드에 추가되는 배열 경계와 같은 런타임 유형 정보의 숨겨진 필드를 추가로 도입할 수 있는...을 나타내는 각 인수에 대한 추가 구문 분석 및 코드 생성 트리를 만듭니다.이 결합된 트리는 매개 변수 값을 프레임에 복사하기 위해 생성되는 코드를 결정하고, 프롤로그는 임의의 또는 마지막으로 명명된 매개 변수에서 시작하는 va_list를 생성하기 위해 va_start에 필요한 것을 설정하며, va_arg()의 각 호출은 다음에서 유효성 검사에 사용되는 매개 변수 특정 숨겨진 필드를 참조하는 인라인 코드를 생성합니다.컴파일 시간은 예상 반환이 컴파일 중인 표현 사용과 호환되는 할당이며, 필요한 인수 프로모션/coerc을 수행합니다.명명된 필드 값 크기와 숨겨진 필드 크기의 합에 따라 호출 후에 컴파일되거나 호출 정리 모델의 함수 에필로그에서 반환 시 프레임을 조정할 값이 결정됩니다.
이러한 각 단계에는 각 인수에 스택의 첫 번째 명명된 인수보다 일정 거리가 할당된 고정 크기가 있다고 가정하는 va_start() 및 va_arg()의 단순한 기본 정의를 재정의하는 config/proc/proc.c 및 proc.h 파일에 캡슐화된 프로세서 및 호출 규약 종속성이 있습니다.일부 플랫폼 또는 언어의 경우 고정 크기 스택보다 별도의 malloc()로 구현된 매개 변수 프레임이 더 바람직합니다.또한 이러한 용도는 스레드 안전이 아닙니다. 함수 반환 또는 스레드 중단으로 인해 매개 변수 프레임이 유효하지 않도록 하는 지정되지 않은 방법 없이 va_list 참조를 다른 스레드에 전달하는 것은 안전하지 않습니다.
ReferenceURL : https://stackoverflow.com/questions/12371450/how-are-variable-arguments-implemented-in-gcc
'source' 카테고리의 다른 글
C#에서 HTTP Post 데이터를 가져오는 방법? (0) | 2023.10.26 |
---|---|
C 소비용 C++ 클래스 API 포장 (0) | 2023.10.26 |
현재 카테고리의 워드프레스 게시물만 나열합니다. (0) | 2023.10.26 |
Retrofit 2 - 동적 URL (0) | 2023.10.26 |
목표-C에서 프로토콜/딜러를 확장하는 방법은 무엇입니까? (0) | 2023.10.26 |