(참고 : 함수 포인터의 예를 보여주는 다른 답변을 연결하는 것은 도움이되지 않습니다 . 제 질문은 서로 다른 답변에 표시된 여러 가지 방법에 관한 것이며 그 차이점을 이해하려고 노력하는 것입니다)
함수를 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)
) 로 호출됩니다.이 답변 은 두 가지 예를 보여줍니다.
execute(print)
( 없이 &
) 로 전달됩니다.void f()
( 없이 *
)f()
( 없이 *
) 로 호출됩니다.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
.
f(myFunc)
실제로 사용 한다고 가정합니다 f(&myFunc)
. 또는 이와 비슷한 것을 알고 싶습니다. "표준"방식).f()
또는 함께 (*f)()
?sizeof(print)
) 의 크기 가 항상 1
? 이 경우 실제로 크기는 얼마입니까? (분명히 64 비트 컴퓨터에서 8 바이트가되는 포인터의 크기가 아닙니다.를 사용하면 포인터의 크기를 얻을 수 있습니다 sizeof(&print)
).sizeof(f)
에서 매개 변수가 다음과 같이 선언 되었음에도 불구하고 8 (포인터 크기)을 제공 하는 이유는 무엇입니까? void (f)()
(이 없이는 *
포인터가 아니라고 가정 할 수 있습니다).내 4 개 스 니펫 모두에 실제 차이가 있습니까? 왜 모두 똑같이 행동합니까?
네 개의 코드 조각은 모두 동일합니다.
execute
호출 방법과 관련 하여 C11 표준 의 섹션 6.3.2.1p4에서 다룹니다 .
함수 지정자는 함수 유형이있는 표현식입니다.
sizeof
연산자,_Alignof
연산자 또는 단항&
연산자 의 피연산자 인 경우를 제외하고 유형이 ''함수 반환 유형 ''인 함수 지정자는 ''포인터 대 함수 반환 유형 ''유형이있는 표현식으로 변환됩니다.
이 때문에 부르 execute(print)
거나 execute(&print)
똑같습니다.
의 매개 변수에 대해서는 execute
6.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] 삭제
몇 마디 만하겠습니다