함수를 전달하거나 C에서 함수 포인터를 전달 하시겠습니까?

라파엘 잉

(참고 : 함수 포인터의 예를 보여주는 다른 답변을 연결하는 것은 도움이되지 않습니다 . 제 질문은 서로 다른 답변에 표시된 여러 가지 방법에 관한 것이며 그 차이점을 이해하려고 노력하는 것입니다)

문맥:

함수를 C의 다른 함수에 매개 변수로 전달하는 올바른 방법을 이해하려고합니다 (C ++ 없음). 나는 몇 가지 다른 방법을 보았고 그 차이점은 나에게 명확하지 않습니다.

내 환경

macOS를 실행하고 있습니다

내 컴파일러는 GCC입니다.

$ gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

CFLAGS=-Wall -g -O0내 Makefile에서 사용 하고 있습니다.

내 코드 예제

다음 4 개의 스 니펫은 모두 동일한 결과를 생성합니다 (최소한 동일한 가시적 출력). 샘플 간의 유일한 차이점은 선언과 execute함수 호출 모두에 있습니다. 이 샘플을 구분하기 위해 처음에 각 샘플을 호출하는 방식으로 따옴표에 포함했습니다 (이름이 잘못되었을 수 있음).

그것들은 실제로 다음의 4 가지 순열입니다.

  • execute수신 할 함수 선언 void f()또는void (*f)()
  • 함수 호출 execute과를 execute(print)execute(&print)

모든 경우에 함수는으로 호출 f()되지 않고으로 호출됩니다 (*f)(). 그러나 나는 (*f)()동일한 결과를 산출 한으로도 테스트했습니다 . 따라서 8 개의 순열 , 실제로 (간결성을 위해 여기에 4 개만 표시)

  • 스 니펫 1 : " 포인터 없이 전달 , 포인터 없이 수신 "

    #include <stdio.h>
    
    void execute(void f()) {
        printf("2 %p %lu\n", f, sizeof(f));
        f();
    }
    
    void print() {
        printf("Hello!\n");
    }
    
    int main() {
        printf("1 %p %lu\n", print, sizeof(print));
        execute(print);
        return 0;
    }
    
  • 조각 2 : "통과 와 함께 포인터 및 수신 대한 포인터"

    #include <stdio.h>
    
    void execute(void (*f)()) {
        printf("2 %p %lu\n", f, sizeof(f));
        f();
    }
    
    void print() {
        printf("Hello!\n");
    }
    
    int main() {
        printf("1 %p %lu\n", print, sizeof(print));
        execute(&print);
        return 0;
    }
    
  • 조각 3 : "통과 와 함께 포인터, 수신 하지 않고 포인터"

    #include <stdio.h>
    
    void execute(void (f)()) {
        printf("2 %p %lu\n", f, sizeof(f));
        f();
    }
    
    void print() {
        printf("Hello!\n");
    }
    
    int main() {
        printf("1 %p %lu\n", print, sizeof(print));
        execute(&print);
        return 0;
    }
    
  • 조각 4 : "통과 하지 않고 포인터 및 수신 대한 포인터"

    #include <stdio.h>
    
    void execute(void (*f)()) {
        printf("2 %p %lu\n", f, sizeof(f));
        f();
    }
    
    void print() {
        printf("Hello!\n");
    }
    
    int main() {
        printf("1 %p %lu\n", print, sizeof(print));
        execute(print);
        return 0;
    }
    

모든 예 :

  • 프로그램이 오류나 경고없이 컴파일되고 실행됩니다.
  • Hello 올바르게 인쇄됩니다
  • 에 인쇄 된 값 %p이 첫 번째 및 두 번째 인쇄에서 모두 동일합니다.
  • sizeof 첫 번째 인쇄에서 항상 1입니다.
  • sizeof 두 번째 인쇄에서는 항상 8입니다.

내가 읽은 것

여러 예제 (Wikipedia, StackOverflow, StackOverflow 답변에 링크 된 기타 리소스)를 읽었으며 많은 예제가 다른 예제를 보여줍니다. 내 질문은 정확히 그 차이점을 이해하기위한 것 입니다.

함수 포인터에 대한 위키 백과의 문서 (내게로 간체) 4 니펫을 유사한 예를 보여줍니다 :

#include <math.h>
#include <stdio.h>

double compute_sum(double (*funcp)(double), double lo, double hi) {
  // ... more code
  double y = funcp(x);
  // ... more code
}

int main(void) {
  compute_sum(sin, 0.0, 1.0);
  compute_sum(cos, 0.0, 1.0);
  return 0;
}

노트 :

  • 인수는 compute_sum(sin, 0.0, 1.0)( on 없이 ) 로 전달됩니다.&sin
  • 매개 변수가 선언됩니다 double (*funcp)(double)( 사용 * )
  • 매개 변수는 funcp(x)( 없이 * , 그래서 아니오 (*funcp)(x)) 로 호출됩니다.

동일한 Wikipedia 기사의 이후 예제에서는 &추가 설명없이 함수를 전달할 때가 필요하지 않음을 알려줍니다 .

// This declares 'F', a function that accepts a 'char' and returns an 'int'. Definition is elsewhere.
int F(char c);

// This defines 'Fn', a type of function that accepts a 'char' and returns an 'int'.
typedef int Fn(char c);

// This defines 'fn', a variable of type pointer-to-'Fn', and assigns the address of 'F' to it.
Fn *fn = &F;      // Note '&' not required - but it highlights what is being done.

// ... more code

// This defines 'Call', a function that accepts a pointer-to-'Fn', calls it, and returns the result
int Call(Fn *fn, char c) {
  return fn(c);
} // Call(fn, c)

// This calls function 'Call', passing in 'F' and assigning the result to 'call'
int call = Call(&F, 'A');   // Again, '&' is not required

// ... more code

노트 :

  • 인수는 Call(&F, 'A')( with & on F) 로 전달됩니다.
  • 매개 변수가 선언됩니다 Fn *fn( 사용 * )
  • 매개 변수는 fn(c)( 없이 (*fn) ) 로 호출됩니다.

이 대답 :

  • 인수는 func(print)( on 없이 ) 로 전달됩니다.&print
  • 매개 변수가 선언됩니다 void (*f)(int)( 사용 * )
  • 매개 변수는 (*f)(ctr)( 와 함께 (*fn) ) 로 호출됩니다.

이 답변 은 두 가지 예를 보여줍니다.

  • 첫 번째는 내 스 니펫 1과 같습니다.
    • 인수는 execute(print)( 없이 & ) 로 전달됩니다.
    • 매개 변수가 선언 됨 void f()( 없이 * )
    • 매개 변수는 f()( 없이 * ) 로 호출됩니다.
  • 두 번째는 내 스 니펫 2와 같습니다.
    • 인수는 execute(&print)( 와 함께 & ) 로 전달됩니다.
    • 매개 변수가 선언됩니다 void (*f)()( 사용 * )
    • 매개 변수는 f()( 없이 * ) 로 호출됩니다.

이 대답 :

  • 함수 인수를 전달하는 방법에 대한 예가 없습니다 (포함 또는 제외 &)
  • 매개 변수가 선언됩니다 int (*functionPtr)(int, int)( 사용 * )
  • 매개 변수는 (*functionPtr)(2, 3)( 와 함께 * ) 로 호출됩니다.

이 링크 된 자료는 답변 중 하나에서 찾았습니다 (실제로 C ++이지만 함수 포인터와 관련된 C ++ 특정 사항을 사용하지 않습니다).

  • 인수는 &Minus( 와 함께 & ) 로 전달됩니다.
  • 매개 변수가 선언됩니다 float (*pt2Func)(float, float)( 사용 * )
  • 매개 변수는 pt2Func(a, b)( 없이 * ) 로 호출됩니다.

여기서는 사용 여부 &*함수를 인수로 전달 / 함수를 매개 변수로 수신 / 함수를 매개 변수로 호출 할 때 의 최소 ​​5 가지 조합을 제공하는 7 가지 예제를 제공했습니다 .

이 모든 것에서 내가 이해하는 것

이전 섹션에서 StackOverflow에 대한 가장 관련성이 높은 질문 / 답변과 내가 링크 한 다른 자료에 대한 설명에 동의하지 않았다고 생각합니다 (대부분 StackOverflow의 답변에 링크 됨).

이 4 가지 방법은 모두 컴파일러에 의해 동일하게 처리되거나 간단한 예제에서는 나타나지 않는 매우 미묘한 차이가있는 것 같습니다.

두 번째 인쇄물 이 스 니펫 2와 4로 인쇄 sizeof(f)되는 이유를 이해합니다 8. 이것은 64 비트 시스템의 포인터 크기입니다. 그러나 execute함수가 *(스 니펫 1 및 3) 없이 매개 변수를 선언하는 경우에도 두 번째 인쇄물이 포인터의 크기를 인쇄하는 이유를 이해하지 못하고 첫 번째 인쇄에서 함수 변수가 다음 sizeof과 같은 이유를 이해하지 못합니다. 1.

내 질문

  1. 내 4 개 스 니펫 모두에 실제 차이가 있습니까? 왜 모두 똑같이 행동합니까?
    • 실제 런타임 차이가 있다면 각각에서 무슨 일이 일어나고 있는지 설명해 주시겠습니까?
    • 런타임 차이가 없다면 컴파일러가 모든 경우에 똑같이 동작하도록하기 위해 수행 한 작업을 설명해 주시겠습니까 (컴파일러가를보고 f(myFunc)실제로 사용 한다고 가정합니다 f(&myFunc). 또는 이와 비슷한 것을 알고 싶습니다. "표준"방식).
  2. 4 개의 스 니펫 중 어떤 것을 사용해야하며 그 이유는 무엇입니까?
  3. 전달 된 함수를 호출 할 필요가 내가 가진 매개 변수를 통해 수신 f()또는 함께 (*f)()?
  4. 각 조각의 첫 번째 인쇄에서 함수 변수 ( sizeof(print)) 의 크기 가 항상 1? 이 경우 실제로 크기는 얼마입니까? (분명히 64 비트 컴퓨터에서 8 바이트가되는 포인터의 크기가 아닙니다.를 사용하면 포인터의 크기를 얻을 수 있습니다 sizeof(&print)).
  5. 두 번째 인쇄에서 스 니펫 1과 3 sizeof(f)에서 매개 변수가 다음과 같이 선언 되었음에도 불구하고 8 (포인터 크기)을 제공 하는 이유는 무엇입니까? void (f)()(이 없이는 *포인터가 아니라고 가정 할 수 있습니다).
dbush

내 4 개 스 니펫 모두에 실제 차이가 있습니까? 왜 모두 똑같이 행동합니까?

네 개의 코드 조각은 모두 동일합니다.

execute호출 방법과 관련 하여 C11 표준 의 섹션 6.3.2.1p4에서 다룹니다 .

함수 지정자는 함수 유형이있는 표현식입니다. sizeof연산자, _Alignof연산자 또는 단항 &연산자 의 피연산자 인 경우를 제외하고 유형이 ''함수 반환 유형 ''인 함수 지정자는 ''포인터 대 함수 반환 유형 ''유형이있는 표현식으로 변환됩니다.

이 때문에 부르 execute(print)거나 execute(&print)똑같습니다.

의 매개 변수에 대해서는 execute6.7.6.3p8 섹션에서 다룹니다.

``함수 반환 유형 ''으로 매개 변수의 선언은 6.3.2.1에서와 같이``함수 반환 유형에 대한 포인터 ''로 조정되어야합니다.

그래서 이것은 의미 void execute(void (*f)())void execute(void f())동일합니다.

4 개의 스 니펫 중 어떤 것을 사용해야하며 그 이유는 무엇입니까?

이것은 스타일의 문제인 경향이 있지만, 개인적으로 변수와 매개 변수를 함수 유형이 아닌 함수에 대한 포인터 유형으로 선언하고 연산자 주소없이 함수 이름을 전달합니다.

f () 또는 (* f) ()를 사용하여 매개 변수를 통해받은 전달 된 함수를 호출해야합니까?

이것은 또한 스타일의 문제입니다. f()읽기 쉽기 때문에 함께 갈 것입니다 .

각 스 니펫의 첫 번째 인쇄에서 함수 변수 (sizeof (print))의 크기가 항상 1 인 이유는 무엇입니까? 이 경우 실제로 크기는 얼마입니까? (분명히 64 비트 컴퓨터에서 8 바이트가되는 포인터의 크기가 아닙니다. sizeof (& print)를 사용하면 포인터의 크기를 얻을 수 있습니다.)

사용 sizeof하는 기능 지정자에 명시 적으로 섹션 6.5.3.4p1에 따라 허용되지 않습니다 :

sizeof연산자 함수 타입이있는 식에 적용되지 않는다 이러한 유형의 괄호 이름 또는 비트 필드 멤버를 나타내고, 식, 또는 불완전한 타입. _Alignof연산자 함수 타입 또는 불완전한 유형에 적용되지 않는다.

그렇게하면 정의되지 않은 동작이 발생 합니다.

스 니펫 1과 3에서 두 번째 인쇄에서 sizeof (f)는 매개 변수가 void (f) ()로 선언 되었음에도 불구하고 8 (포인터의 크기)을 제공합니다 (따라서 * 없이는 가정 할 수 있음). 포인터가 아닙니다)

이것은 위의 6.7.6.3p8로 거슬러 올라갑니다. 즉, 함수 유형의 함수 매개 변수가 함수 유형 포인터로 변환되어 크기를 얻습니다.

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

C에서 함수에 포인터를 전달하는 방법

분류에서Dev

참조를받는 함수에 대한 포인터를 전달 하시겠습니까?

분류에서Dev

함수 인수에 목록을 전달 하시겠습니까?

분류에서Dev

배열을 함수에 전달하거나 범위 내에 있는지 확인 하시겠습니까?

분류에서Dev

벡터를 기대하는 함수에 포인터 전달

분류에서Dev

컨트롤러 함수에 변수를 전달 하시겠습니까?

분류에서Dev

함수에 문자 포인터를 전달하는 방법

분류에서Dev

함수 c / c ++에서 포인터를 전달하는 동안 "double ** to double *"오류

분류에서Dev

버튼 요소를 콜백 함수에 전달 하시겠습니까?

분류에서Dev

jquery 키 이벤트에 함수를 전달 하시겠습니까?

분류에서Dev

C ++에서 참조를 허용하는 함수에 포인터 전달

분류에서Dev

void * 포인터를 기대하는 함수에 함수 객체를 어떻게 전달할 수 있습니까?

분류에서Dev

mapply에서 함수 이름을 인수로 전달 하시겠습니까?

분류에서Dev

함수에 포인터를 전달하고 함수가 반환 된 후 변경

분류에서Dev

인수를 전달하거나 전달하지 않는 함수-PHP

분류에서Dev

콜백에 인수를 전달 하시겠습니까?

분류에서Dev

tableView에서 prepareForSegue로 변수를 전달 하시겠습니까?

분류에서Dev

다음 함수에 인수를 전달하는 함수

분류에서Dev

쉬운 방법으로 여러 데이터 변수를 AJAX 함수에 전달 하시겠습니까?

분류에서Dev

모든 bash 함수의 인수를 docker exec 명령에 전달 하시겠습니까?

분류에서Dev

Python-ctypes를 사용하여 C의 함수에 imgdata 포인터 전달

분류에서Dev

함수에 배열 주소를 전달하고 포인터를 반환

분류에서Dev

내구성 함수의 orchestrationTrigger 함수에서 httpTrigger 함수에 의해 생성 된 orchestrationID를 전달하거나 어떻게 든 가져올 수 있습니까?

분류에서Dev

필터 함수에 인수를 전달하는 Javascript?

분류에서Dev

구조를 Java 함수에 인수로 전달하거나 jni에서 Java로 반환하는 방법

분류에서Dev

dll에 매개 변수를 전달 하시겠습니까?

분류에서Dev

main () C ++에서 여러 함수를 통해 포인터 전달

분류에서Dev

Kotlin에서 함수에 함수를 전달하는 방법

분류에서Dev

C에서 함수 인수로 구조체 포인터를 전달할 수 없습니다.

Related 관련 기사

  1. 1

    C에서 함수에 포인터를 전달하는 방법

  2. 2

    참조를받는 함수에 대한 포인터를 전달 하시겠습니까?

  3. 3

    함수 인수에 목록을 전달 하시겠습니까?

  4. 4

    배열을 함수에 전달하거나 범위 내에 있는지 확인 하시겠습니까?

  5. 5

    벡터를 기대하는 함수에 포인터 전달

  6. 6

    컨트롤러 함수에 변수를 전달 하시겠습니까?

  7. 7

    함수에 문자 포인터를 전달하는 방법

  8. 8

    함수 c / c ++에서 포인터를 전달하는 동안 "double ** to double *"오류

  9. 9

    버튼 요소를 콜백 함수에 전달 하시겠습니까?

  10. 10

    jquery 키 이벤트에 함수를 전달 하시겠습니까?

  11. 11

    C ++에서 참조를 허용하는 함수에 포인터 전달

  12. 12

    void * 포인터를 기대하는 함수에 함수 객체를 어떻게 전달할 수 있습니까?

  13. 13

    mapply에서 함수 이름을 인수로 전달 하시겠습니까?

  14. 14

    함수에 포인터를 전달하고 함수가 반환 된 후 변경

  15. 15

    인수를 전달하거나 전달하지 않는 함수-PHP

  16. 16

    콜백에 인수를 전달 하시겠습니까?

  17. 17

    tableView에서 prepareForSegue로 변수를 전달 하시겠습니까?

  18. 18

    다음 함수에 인수를 전달하는 함수

  19. 19

    쉬운 방법으로 여러 데이터 변수를 AJAX 함수에 전달 하시겠습니까?

  20. 20

    모든 bash 함수의 인수를 docker exec 명령에 전달 하시겠습니까?

  21. 21

    Python-ctypes를 사용하여 C의 함수에 imgdata 포인터 전달

  22. 22

    함수에 배열 주소를 전달하고 포인터를 반환

  23. 23

    내구성 함수의 orchestrationTrigger 함수에서 httpTrigger 함수에 의해 생성 된 orchestrationID를 전달하거나 어떻게 든 가져올 수 있습니까?

  24. 24

    필터 함수에 인수를 전달하는 Javascript?

  25. 25

    구조를 Java 함수에 인수로 전달하거나 jni에서 Java로 반환하는 방법

  26. 26

    dll에 매개 변수를 전달 하시겠습니까?

  27. 27

    main () C ++에서 여러 함수를 통해 포인터 전달

  28. 28

    Kotlin에서 함수에 함수를 전달하는 방법

  29. 29

    C에서 함수 인수로 구조체 포인터를 전달할 수 없습니다.

뜨겁다태그

보관