source

가변 인수는 gcc에서 어떻게 구현됩니까?

nicesource 2023. 10. 26. 21:15
반응형

가변 인수는 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_startmacro는 첫 번째 함수 파라미터에 대한 포인터를 설정합니다. 예:-

 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_endmacro는 그냥 설정합니다.p치에 입니다.NULL.

주의:

  1. 컴파일러와 일부 RISC CPU를 최적화하면 스택을 사용하지 않고 레지스터에 파라미터를 저장합니다.재의 ....매개 변수는 이 기능을 끄고 컴파일러가 스택을 사용하도록 합니다.

스택에 인수가 전달되면,va_함수들(대부분 매크로로 구현됨)은 단순히 개인 스택 포인터를 조작합니다.이 개인 스택 포인터는 전달된 인수에서 다음으로 저장됩니다.va_start,그리고 나서.va_arg매개변수를 반복할 때 "스택"에서 인수를 "pops"합니다.

당신이 그 함수를 부른다고 가정해 보겠습니다.max다음과 같은 세 가지 매개 변수를 사용합니다.

max(a, b, c);

인사이드 더maxfunction, 스택은 기본적으로 다음과 같습니다.

+-----+|  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

반응형