programing

함수 포인터의 역참조는 어떻게 이루어집니까?

prostudy 2022. 8. 27. 08:51
반응형

함수 포인터의 역참조는 어떻게 이루어집니까?

기능 포인터를 역참조하는 이유는 무엇이며, 어떻게 하면 아무것도 하지 않는 것일까요?

이게 바로 내가 말하고자 하는 것이다.

#include<stdio.h>

void hello() { printf("hello"); }

int main(void) { 
    (*****hello)(); 
}

여기 댓글에서:

함수 포인터들은 아주 잘 참조되지만, 결과 함수 지정자는 즉시 함수 포인터로 다시 변환됩니다.


그리고 여기 답변에서:

역참조(생각하는 방식)란 함수의 포인터가 데이터 메모리인 것처럼 CODE 메모리에 액세스하는 것을 의미합니다.

함수 포인터는 이러한 방식으로 참조되지 않습니다.대신, 그것은 불려진다.

"dereference"라는 이름과 "call"을 나란히 사용합니다.괜찮아요.

어쨌든: C는 함수 이름 식별자 및 변수 홀딩 함수의 포인터가 동일한 것을 의미하도록 설계되어 있습니다: CODE 메모리에 대한 주소.또한 식별자 또는 변수에서 call() 구문을 사용하여 해당 메모리로 점프할 수 있습니다.


함수 포인터의 역참조는 정확히 어떻게 작동합니까?

그건 올바른 질문이 아니야.적어도 C에게 있어 올바른 질문은

rvalue 컨텍스트의 함수 값은 어떻게 됩니까?

(rvalue 컨텍스트는 이름 또는 기타 참조가 표시되는 장소이며, 로케이션이 아닌 값으로 사용됩니다.기본적으로 할당의 좌측을 제외한 모든 장소입니다.이름 자체는 과제의 오른쪽에서 유래합니다.)

"rvalue" "rvalue" "rvalue" "rvalue" "rvalue" "rvalue" "rvalue" "r" "rvalue" "이는 즉시 암묵적으로 원래 함수 값에 대한 포인터로 변환됩니다.를 「」로 .*그러면 동일한 함수 값이 다시 반환되고 즉시 암시적으로 포인터로 변환됩니다.원하는 만큼 할 수 있어요

다음과 같은 두 가지 유사한 실험을 시도할 수 있습니다.

  • lvalue 컨텍스트에서 함수 포인터를 참조 해제했을 경우(할당의 좌측). (함수는 불변이라는 점에 유의하면 답은 예상한 것에 관한 것입니다.)

  • 배열 값도 lvalue 컨텍스트에서 포인터로 변환되지만 배열에 대한 포인터가 아니라 요소 유형에 대한 포인터로 변환됩니다.따라서 역참조하면 배열이 아닌 요소가 제공되며, 사용자가 보여주는 광기는 발생하지 않습니다.

이게 도움이 됐으면 좋겠다.

추신. 함수의 값이 왜 포인터로 암묵적으로 변환되는지에 대한 답은 함수 포인터를 사용하는 사람들에게는 사용하지 않아도 되는 것이 매우 편리하다는 것입니다.&은 어디에나 있다. 포인터는 변환되기 에 콜 의 함수 포인터는 함수 값으로 자동 변환되기 때문에 콜 포지션의 함수 포인터를 사용할 *함수 포인터를 통해 호출합니다.

P.P.S. C 함수와 달리 C++ 함수는 과부하가 될 수 있으며, C++에서의 의미론 동작에 대해 코멘트할 자격이 없습니다.

몇 가지 암묵적인 변환으로 이루어집니다.실제로, C 표준에 따르면:

ISO/IEC 2011, 섹션 6.3.2.1 값, 어레이 및 기능 지정자, 단락 4

함수 지정자는 함수 유형을 가진 표현식입니다.피연산자의 피연산자일 때를 제외하고sizeof 또는 " " " " " " " 。&연산자는 "함수 복귀형" 타입의 함수 지정자를 "함수 복귀형" 타입의 식으로 변환한다.

다음 코드를 고려합니다.

void func(void);

int main(void)
{
    void (*ptr)(void) = func;
    return 0;
}

지정자 「」가 func는 "function returning" 타입이지만 즉시 "function returning" 타입의 표현으로 변환됩니다.그러나 만약 당신이 쓴다면

void (*ptr)(void) = &func;

지정자func"함수 반환" 유형을 가지지만 단항은&연산자는 해당 함수의 주소를 명시적으로 가져가고, 결과적으로 "function returning"이라는 유형을 생성합니다.

이는 C 표준에 기재되어 있습니다.

ISO/IEC 2011, 섹션 6.5.3.2 주소 및 간접 운영자, 단락 3

★★★&연산자는 오퍼랜드의 주소를 제공합니다.피연산자의 유형이 "type"인 경우 결과 유형은 "type에 포인터"입니다.

특히 함수 포인터의 역참조는 장황합니다.C 규격에 따라:

ISO/IEC 2011, 섹션 6.5.2.2 함수 호출, 단락 1

호출된 함수를 나타내는 표현식은 "함수에 포인터" 유형을 가져야 하며 배열 유형 이외의 완전한 객체 유형을 반환해야 합니다.대부분의 경우 이는 함수 지정자인 식별자를 변환한 결과입니다.

ISO/IEC 2011, 섹션 6.5.3.2 주소 및 간접 운영자, 단락 4

★★★*는 indirectiondirection을 나타냅니다.피연산자가 함수를 가리킬 경우 결과는 함수 지정자가 됩니다.

그래서 글을 쓸 때

ptr();

의 콜은 평가됩니다.왜냐하면 함수의 콜은 암묵적인 이 필요하기 때문입니다.ptr는 이미 기능에 대한 포인터입니다.명시적으로 참조를 해제하는 경우

(*ptr)();

그 후, 역참조에서는 「함수 반환」타입이 생성되어 곧바로 「함수 반환」타입으로 변환되어 함수 호출이 발생합니다.x 단항으로 구성된 식을 쓸 때*음음음 such such such such such such such 등의

(****ptr)();

암묵적인 변환을 x회 반복하면 됩니다.


호출 함수에 함수 포인터가 포함되어 있는 것은 당연합니다.함수를 실행하기 전에 프로그램은 함수의 모든 파라미터를 기록된 역순으로 스택에 푸시합니다.그 후 프로그램은 a를 발행한다.call어떤 기능을 시작할 것인지를 나타내는 명령입니다.call명령은 다음 두 가지 작업을 수행합니다.

  1. 먼저 다음 명령의 주소, 즉 반환 주소를 스택에 푸시합니다.
  2. 그런 다음 명령 포인터를 수정합니다.%eip기능의 시작을 가리킵니다.

함수를 호출하는 것은 메모리 주소인 명령 포인터를 수정하는 것을 포함하기 때문에 컴파일러가 함수의 지정자를 함수의 포인터로 암묵적으로 변환하는 것은 의미가 있다.


이러한 암묵적인 변환이 있는 것처럼 보이지 않는 경우에도 C++에서는 변수를 캡슐화하기 위해 구조 식별자에 의해 정의된 네임스페이스를 이용하는 것이 편리합니다.

다음 코드를 고려합니다.

void create_person(void);
void update_person(void);
void delete_person(void);

struct Person {
    void (*create)(void);
    void (*update)(void);
    void (*delete)(void);
};

static struct Person person = {
    .create = &create_person,
    .update = &update_person,
    .delete = &delete_person,
};

int main(void)
{
    person.create();
    person.update();
    person.delete();
    return 0;
}

다른 변환 유닛에서 라이브러리의 구현을 숨기고 함수에 대한 포인터를 캡슐화하는 구조만 노출하도록 선택할 수 있으며 실제 함수 지정자 대신 사용할 수 있습니다.

C++03 4 4.3/1:

함수 유형 T의 l 값은 "포인터 T" 유형의 r 값으로 변환할 수 있습니다.결과는 함수에 대한 포인터입니다.

단항과 같은 함수 참조에서 잘못된 작업을 시도한 경우*교환, 언어가 가장 먼저 시도하는 것은 표준 변환입니다.이건 마치 다른 사람을 변환하는 것과 같아int에 추가할 때float.사용.*함수 참조에서는 대신 언어가 포인터를 가져가도록 합니다.이 예에서는 정사각형 1입니다.

함수 포인터를 할당하는 경우도 이에 해당합니다.

void f() {
    void (*recurse)() = f; // "f" is a reference; implicitly convert to ptr.
    recurse(); // call operator is defined for pointers
}

방법은 반대로 동작하지 않습니다.

void f() {
    void (&recurse)() = &f; // "&f" is a pointer; ERROR can't convert to ref.
    recurse(); // OK - call operator is *separately* defined for references
}

함수 참조 변수는 (이론적으로는 테스트한 적이 없습니다) 컴파일러에 대해 동봉된 범위에서 초기화할 경우 간접적인 분기가 불필요할 수 있음을 암시하기 때문에 좋습니다.

C99에서는 함수 포인터를 참조하면 함수 지정자가 생성됩니다.§6.3.2.1/4:

함수 지정자는 함수 유형을 가진 표현식입니다.연산자 크기 또는 단항 & 연산자의 피연산자일 때를 제외하고, "함수 복귀형" 타입의 함수 지정자는 "함수 복귀형" 타입의 식으로 변환된다.

이것은 노먼의 대답에 가깝지만, 특히 C99에는 rvalue의 개념이 없습니다.

컴파일러 작가의 입장이 되어 보세요.함수 포인터는 잘 정의된 의미를 가지며, 기계 코드를 나타내는 바이트 블록에 대한 포인터입니다.

프로그래머가 함수 포인터를 참조할 때 어떻게 합니까?기계 코드의 첫 번째(또는 8) 바이트를 포인터로 재해석합니까?이것이 효과가 없을 확률은 약 20억분의 1이다.UB를 선언하시겠습니까?이미 많은 것들이 돌고 있다.아니면 그냥 모른 척 하는 거야?정답을 아시잖아요.

함수 포인터의 역참조는 정확히 어떻게 작동합니까?

두 단계.첫 번째 단계는 컴파일 시이고 두 번째 단계는 런타임입니다.

1단계에서 컴파일러는 포인터와 그 포인터가 참조되지 않는 컨텍스트를 가지고 있는 것을 확인합니다(예:(*pFoo)()따라서 이 상황에 맞는 코드를 생성합니다.이 코드는 2단계에서 사용됩니다.

스텝 2에서는 실행 시 코드가 실행됩니다.포인터에는 다음에 실행할 함수를 나타내는 바이트가 포함되어 있습니다.이러한 바이트는 CPU에 로드됩니다.일반적인 경우는, CPU에 명시적으로,CALL [register]설명.이러한 시스템에서 기능 포인터는 단순히 메모리에 있는 함수의 주소가 될 수 있으며, 디리펜스 코드는 그 주소를 레지스터에 로드한 후, 그 뒤에 이어CALL [register]설명.

언급URL : https://stackoverflow.com/questions/2795575/how-does-dereferencing-of-a-function-pointer-happen

반응형