함수포인터 문제..

나빌레라의 이미지

과연 이게 함수포인터에 관한 문제인지조차 의심스럽지만 아무래도 함수 포인터에 관련된 문제 같아서 질문드립니다.

후배가 물어본건데, 저도 답을 몰라, 답을 못했줬거든요...(아..쪽팔려...)

근데 저도 궁금해서, 고수분들께 물어봅니다.

#include <stdio.h>

void f(void);
void g(void);
void h(void);
int main(void)
{
    (*f)();
    return 0;
}
void f(void)
{
    printf("Hello from f().\n");
    (((*g)))();
}
void g(void)
{
    printf("Hello from g().\n");
    (*(*(*h)))();
}
void h(void)
{
    printf("Hello from h().\n");
}

이런 소스 인데... 함수포인터에 저렇게 *을 계속 붙이면 포인터가 가르치는 주소의 주소의 주소의 값을 찾아가기때문에 잘못된 값이 나오는거 아닌가요?

그런데 결과는..

Hello from f().
Hello from g().
Hello from h().

이렇게 잘 만 나옵니다...

그리고 마지막 함수에

(*(*(*h)))();

(*(*h))();

라고 해도 에러 없이 결과 잘 나오네요..

대체 이유가 뭘까요?

함수포인터를 사용해 개발해본 경험도 있는데, 막상 저렇게 누군가가 물어보니,

이유를 딱 설명하기 난감해지네요...

함수포인터의 괄호밖 *은 리턴값인가... 하는 생각도 들고...[/code]

cydra의 이미지

함수포인터의 명시적 표현은 앞에 *를 붙이는 걸로 알고 있구요..

암시적으로 함수의 이름자체가 함수 포인터라고 알고 있습니다...

즉 함수 이름 앞에 *를 붙이는 것은 함수 포인터라는 것을 명시적으로 나타내기

위해 사용하는 것으로 알고 있습니다. h()라는 함수가 있을때

(*f)()이렇게 호출하는 것과 f()는 실행결과가 같죠..

(*(*f))() 와 (*f)()도 같은 논리로 하면 같죠 ..

그러므로 (*(*f))() 와 f()도 같다고 ;; 생각이 되네요 ..

서명 없음

나빌레라의 이미지

함수 이름 앞에 *를 붙이는 것은 함수 포인터라는 것을 명시적으로 나타내기

위해 사용하는 것으로 알고 있습니다. h()라는 함수가 있을때

(*f)()이렇게 호출하는 것과 f()는 실행결과가 같죠..

(*(*f))() 와 (*f)()도 같은 논리로 하면 같죠 .. 

함수의 이름이 암시적으로 함수 포인터라는건 알겠습니다.

그래서 f()와 (*f)() 가 같다는건 이해가 가는데,

그 앞에 * 또붙으면 마치 이중포인터 처럼

포인터의 포인터 아닌가요?

함수포인터만 앞에 *가 여러개 붙어도 무시하고 처리한다면,

C언어 자체의 포인터 변수 규칙이 일관성이 없어지는듯 한데요.

----------------------
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

wooix의 이미지

(****************************(*f))()
(*(*f))()
(*f)()

위의 셋은 똑같은 곳을 호출합니다.

assemble 코드를 보니

Call @ILT + offset(f) 로 나오는군요.

compiler가 해석을 하는데 있어서의 문제인듯합니다.

이중 pointer혹은 다중 pointer로 잡지 않고

*뒤에 의미없는 문자열이 이어져있을때는 null offset과 같은 걸로 처리하는것 같군요..

평온하다~

나빌레라의 이미지

(****************************(*f))()
(*(*f))()
(*f)()

위의 셋은 똑같은 곳을 호출합니다.

assemble 코드를 보니

Call @ILT + offset(f) 로 나오는군요.

compiler가 해석을 하는데 있어서의 문제인듯합니다. 

수고스럽게, 디스어셈블까지 해주셔서 너무 감사합니다.

그렇다면 결론은, 함수포인터에 있어서 만큼은, * 이 몇개이건 간에,

그냥 단순한 포인터 변수로 취급한다는 건가요?

(좋은건지..나쁜건지...)

----------------------
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

전웅의 이미지

http://c-expert.uos.ac.kr/board/hclc_post_board/ScB.cgi?process=disp_data&datanum=499

를 참고하시기 바랍니다. C 언어의 문법적 정의를 적용하면 나오는 당연한 결과입니다.

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

verena의 이미지

컴파일러가 컴파일시에 code optimizing을 행함으로 인해 복수개의 *가 생략되는걸로 알고 있습니다. 다음의 코드가 컴파일시 생략되는것과 마찬가지로...

for(int i=0; i < 10000; i++);

물론 코드 옵티마이즈 단계를 컴파일시에 명시하면 달라 집니다.

lsj0713의 이미지

verena wrote:
컴파일러가 컴파일시에 code optimizing을 행함으로 인해 복수개의 *가 생략되는걸로 알고 있습니다. 다음의 코드가 컴파일시 생략되는것과 마찬가지로...

for(int i=0; i < 10000; i++);

물론 코드 옵티마이즈 단계를 컴파일시에 명시하면 달라 집니다.

아니오. 다릅니다. 컴파일러의 옵티마이즈와는 상관없이 C언어 자체적인 정의에 의해 (*******func_ptr)(); 과 같은 코드를 허용합니다. 위의 전웅님께서 올리신 글을 잘 읽어보세요.

verena의 이미지

아 그렇군요. 지금까지 코드 옵티마이즈와 관련해서 생각을 했는데...

이번기회에 함수포인터에 대한 C언어에서의 정의를 자세히 알게 되었군요. 감사합니다. ^^.

그런데 정말 궁금한것은.... 해당 링크된 사이트에서도 나타나질 않는데...

왜 (******평선포인터)(); 와 같은 형태를 취하여 함수를 호출하는지는 안 나타나 있네요.

왜 저런 형태를 사용하죠? 식별자가 pointer to pointer to pointer.... 라 하더라도, 실제로 이런 자료형이 크게 유용한지도 의문이 듭니다...

전웅의 이미지

verena wrote:
아 그렇군요. 지금까지 코드 옵티마이즈와 관련해서 생각을 했는데...

이번기회에 함수포인터에 대한 C언어에서의 정의를 자세히 알게 되었군요. 감사합니다. ^^.

그런데 정말 궁금한것은.... 해당 링크된 사이트에서도 나타나질 않는데...

왜 (******평선포인터)(); 와 같은 형태를 취하여 함수를 호출하는지는 안 나타나 있네요.

왜 저런 형태를 사용하죠? 식별자가 pointer to pointer to pointer.... 라 하더라도, 실제로 이런 자료형이 크게 유용한지도 의문이 듭니다...

현실적인 유용성은 없겠지만, 어떤 대상체의 데이터형이 실제로 pointer to
pointer to pointer to ... function 인 경우 반드시 위와 같은 수식을
사용해 최소한 poiner to function 이나 function 단계까지 지정해야 함수
호출을 할 수 있습니다.

해당 글에서 제가 인용한 책에 자세히 설명되어있지만, 이와 같이 이상한
형태의 함수 호출 수식이 허락되는 이유는 C 언어가 정의되는 과정에서
발생한 일종의 문법적 부작용이라고 보시면 됩니다. 예를 들어, 배열 첨자
연산인 a[i] 가 포인터 연산인 *((a) + (i)) 로 정의됨으로써 i[a] 라는
기형적 형태의 수식이 허락되는 것과 같은 이치입니다.

C 언어의 표준화 과정에서 함수 호출을 함수형의 function designator 가
아닌 함수 포인터에 적용되는 연산으로 정의하고, (마치 많은 문맥에서
배열이 포인터로 decay 하듯이) function designator 가 거의 대부분의
문맥에서 함수 포인터로 변환되는 것으로 정의함으로써 부작용으로 허락된
수식입니다.

함수 지정자가 아닌 함수 포인터에 함수 호출이 허락되도록 정의한 것은
표준화 이전의 다수의 existing practice 를 반영한 것이며 동시에 함수
포인터가 구조체 멤버 같이 보다 복잡한 문맥에 포함되어 있는 경우,
*(object->f()); 처럼 보기 싫은 수식보다 (마치 실제 함수인 것처럼)
간단한 형태인 object->f(); 로 쓸 수 있도록 허락하려는 의도입니다.

그 과정에서

int f();
int (*pf)();    // 초기화 가정

&f();    // 함수 지정자가 번지 연산자의 피연산자이면 함수 포인터로의
            변환이 이루어지지 않음
(****f)();
(*****pf)();

같은 외관상 이상한 함수 호출 수식이 허락되었지만 현실적인 문제를
일으키지도 않고 마땅히 금지할 논리적 이유가 없기 때문에 i[a] 를
허락하듯이 그냥 허락하게 된 것 뿐입니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.