void*에 함수 포인터와 멤버에 대한 포인터는 왜 대입이 안되나요?

Raewoo의 이미지

The C++ programming language(http://book.naver.com/bookdb/book_detail.nhn?bid=1547949) 책의 5.6절을 보면, "함수에 대한 포인터와 멤버에 대한 포인터는 void*에 대입할 수 없다"는 말이 나오는 데 이것은 왜 안되는 건가요? 어차피 가리키는 것은 물리적 주소이므로 가능해야 하는 것 아닌가 하는 생각이 들어 질문드립니다.

익명 사용자의 이미지

가능한 것보다는 가능하지 않게 하는 편이 더 낫기에 막아둔 것입니다.

가능하게 해서 얻을 수 있는 것은 void *를 함수에 대한 범용 포인터로 쓸 수 있다는 것인데
형변환을 엄격하게 제한하는 C++에서 이것은 이점이 되지 못합니다.
사실 C에서도 범용 함수 포인터로는 void * 보다는 아무 타입의 함수 포인터를 쓰는게 맞습니다. (C에서 모든 타입의 함수 포인터들은 동일한 내부 표현을 가집니다. 단, 형변환 연산자를 제대로 잘 써줘야 합니다)

맴버에 대한 포인터는 서술이 좀 충분치가 않은거 같은데, public 맴버 변수에 대한 포인터를 void *에 대입하는건 당연히 되고, 맴버 함수에 대한 포인터를 void *에 대입하는게 안된다는거 같습니다.

그런데 사실 이건 당연한게 맴버 함수에 대한 포인터 주소값은 다른 포인터 주소값과 크기가 다를수도 있습니다. (다중 상속 등을 하면 그 크기가 달라질 수 있습니다)

Raewoo의 이미지

매우 명쾌한 답변을 주셔서 정말 감사합니다.

익명 사용자의 이미지

C에서 함수 포인터는 void *로 대입(형변환)할 수 있습니다.

위 내용은 아무래도 멤버함수를 void *로 가리킬 수 없다는 내용일 거라 짐작이 되는데요..

가리킬 수 없는 이유는 class를 어떻게 내부적으로 구현할 지는 컴파일러 만드는 사람 마음입니다.
C++ 표준에서는 그 내부 구현까지 정의하지 않았죠.

그렇기 때문에 멤버함수 포인터는 일반 포인터로 가리킬 수 없고
멤버함수 포인터라고 따로 있습니다.

그러니까 그걸 일반 제네릭 포인터로 가리킬 순 없는거죠.

익명 사용자의 이미지

C에서도 함수 포인터가 void *로 항상 변환될 수 있는 것은 아닙니다. void *와 아무 문제 없이 상호변환되는게 보장되는 타입들은 incomplete 타입 또는 object 타입 뿐입니다. 함수 포인터는 이에 해당하지 않습니다.

다만 J.5 Common extensions 항목에서 가능성 있는 확장의 예로 함수 포인터와 void *간의 상호변환을 제시하고 있을 뿐입니다. (그리고 상당수의 컴파일러들이 이 확장을 만족합니다)

오로지 단 한 종류의 값만이 void *에서 함수 포인터로 변환되는게 확실하게 보장되는데, 그건 다들 아시는대로 (void *)0, 즉 널 포인터 상수입니다.

C에서는 모든 종류의 함수 포인터가 서로 상호 변환되기 때문에(단, 호출은 별개의 문제) void*를 범용 함수 포인터로 쓸 이유가 전혀 없고, C++에서는 객체지향이 되는데 범용 함수 포인터를 사용할 이유 자체가 없지요.

익명 사용자의 이미지

void *에 대입이 된다는건 함수 포인터가 void *로 변환될 수 있다는 얘기지
상호변환이 보장된다는 얘기가 아니죠.

C++에서 포인터를 사용하는 것을 두고 객체지향을 가지고 얘기할 수 없습니다.
멤버함수 포인터라는게 존재하기 때문이죠.

익명 사용자의 이미지

> void *에 대입이 된다는건 함수 포인터가 void *로 변환될 수 있다는 얘기지 상호변환이 보장된다는 얘기가 아니죠.

제가 너무 돌려서 말한 모양이군요. 함수 포인터에서 void*로의 변환도 확장일 뿐입니다. 널리 쓰이는 확장이긴 하죠. 그리고 보통 pointer to function에서 void *로 변환될 수 있는 컴파일러는 그 역도 보장을 합니다. 일방통행만 가능하다면 아무 의미가 없기 때문입니다.

어떤 친구들은 이렇게 말하기도 합니다.

http://yosefk.com/c++fqa/function.html#fqa-33.8

근데 너무 널리 쓰이는 확장이라서(dlsym()을 포함해서...) 참 뭐라 말하기 어렵긴 합니다. 틀린건 맞는데 운영체제 차원에서 쓰고 있으니 쓰지 말라고 하기도 뭐하고...

> C++에서 포인터를 사용하는 것을 두고 객체지향을 가지고 얘기할 수 없습니다. 멤버함수 포인터라는게 존재하기 때문이죠.

"C++에서는 객체지향이 되는데 범용 함수 포인터를 사용할 이유 자체가 없지요."라는 내용에 대해 반론하신 건가요? 뭘 말씀하고 싶으신 건지 잘 모르겠습니다. 지금 위의 얘기는 단순히 고정된 타입의 포인터에 대한 얘기가 아니라, 서로 다른 타입의 함수 포인터간 변환에 대한 얘기입니다. 어떤 맴버 함수 포인터에 다른 타입의 맴버 함수 포인터를 담을 수 있나요?

객체지향 언어라면 콜백 함수 포인터 보다는 상속을 통해 해결하는게 맞겠지요. C++의 형변환에 대한 제한이 C보다 강한 이유도 그것 때문이고. 이 맥락에서 객체지향 얘기를 꺼내는게 뭔가 잘못되었나요?

Raewoo의 이미지

http://yosefk.com/c++fqa/function.html#fqa-33.8
위 말을 현재 플랫폼에서는 사용 가능할 수도 있으나,
1. (C|C++의?) 언어 규칙을 위반하고,
2. 2의 보수와 IEEE 부동 소수를 사용하지 않는 마이크로 컨트롤러와 같은 낮은 플랫폼으로의 이식이 필요할 경우 향후 이식성에 문제를 초래할 수도 있으므로 사용하지 말라는 뜻으로 해석해 보았습니다.

익명 사용자의 이미지

[33.8] Can I convert a pointer-to-function to a void*?
함수로의 포인터를 void *형으로 바꿀 수 있습니까?

FAQ: Stop that. No. And don't tell me it worked for you. It's illegal.
제발 그만. 안돼. 그리고 당신한테는 그게 잘 작동한다고 나에게 말하지 마. 그건 잘못된거다.
(역자 주: 문법적으로는 틀려도 특정 컴파일러에서는 확장때문에, 혹은 몇가지 이유로 잘 되는 경우가 많은데, 이런 경우 '나는 코드 돌려보니까 잘 되는데 왜 틀렸다고 하느냐?'라는 질문이 수도 없이 반복되곤 합니다... 그래서 FAQ에서 이런 반응이 나오는 겁니다.)

FQA: C and C++ strongly separate between code and data (so do many languages and hardware processor implementations). A pointer to a data object is not the same as a pointer to a function. However, in the vast majority of implementations their sizes are going to be the same, so it's possible to convert a function pointer to a void*, and then somewhere else convert it back and call the function.

C와 C++은 코드와 데이터를 아주 강하게 구분한다(많은 언어들과 하드웨어 프로세서들이 그러하듯이). 데이터 객체로의 포인터는 함수로의 포인터와 서로 다를 수 있다(역자 주: 아예 하드웨어적으로 서로 분리된 메모리 공간에 위치할 수도 있다). 그러나, 널리 쓰이는 많은 구현체(컴파일러라고 이해하면 편함. 실제로는 하드웨어+운영체제+컴파일러를 포함한, 주변 환경 전체를 뜻함)들에서, 데이터로의 포인터와 함수로의 포인터는 같은 크기를 가지며, 따라서 함수 포인터가 void *형으로, 또는 다시 역으로 변환되어 함수를 호출할 수 있다.

This violates the language rules. So does code assuming 2's complement integers and IEEE floating point. The chance of both kinds of code to actually fail on an interesting platform is low. Admittedly, the code-pointers-are-just-like-data-pointers assumption is less useful than the signed-numbers-can-be-divided-using-right-shift assumption, though. So typically one wouldn't do this kind of cast, after all.

이것은 언어적 규칙을 위반한 것이다. 그러나 2의 보수 정수 표현을 사용한다고 가정하는 코드도 마찬가지이고 실수 표현을 위해 IEEE 754 표준을 사용한다고 가정하는 코드도 마찬가지로 언어적 규칙을 위반한 것이다. 널리 쓰이는 플랫폼에서 이 두가지 가정이 옳지 않을 가능성은 매우 적다. 그러나, 인정하건대, '코드 포인터가 데이터 포인터와 같다는 가정'은 '부호형 정수를 오른쪽 쉬프트를 통해 나눌 수 있다는 가정'보다는 유용하지 않다. 따라서 결국, 일반적으로 이러한 형태의 형변환을 쓸 필요가 없다.

(역자 주: 우리가 코드를 짤 때는 현실적으로 엄밀하게 정의된 언어적 규칙 이외에 추가적으로 언어적 규칙에 없는 몇가지 '가정'을 더 포함하게 됩니다. 위에서 말한것 이외에도 '문자 코드가 ASCII이다' 라던가 '포인터 크기는 32비트이다'라는 것과 같은 것들이죠. 이러한 가정들은 코딩량을 줄여주기도 하지만 반면에 이식성에 문제가 되기도 합니다. 이러한 가정들이 합리적인가 아닌가 하는 것은 각각의 코드의 목적과 성격에 따라 달라집니다. 어떨때는 합리적이었던 가정이 어떨때는 새로 만드는게 나을 정도의 복잡하고 찾기 어려운 버그를 만들 수도 있습니다. 즉 경우에 따라 다릅니다.

이번 경우에 한해서는, 비교적 간단합니다. 쓸 이유가 없습니다. 범용 함수 포인터로는 void * 대신에 그냥 아무 타입의 함수 포인터나 정해서 쓰면 됩니다. 이렇게 한다고 코드가 더 복잡해 지지도 않고 처리속도가 저하되지도 않으며 가독성이 떨어지지도 않습니다. 그것이 dlsym()이 좋지 않은 디자인인 이유입니다. 전혀 그럴 이유가 없는데 그렇게 만들어져 있습니다.

하지만 현실적으로 dlsym()함수, 혹은 winapi 함수들을 쓸 때와 마찬가지로, 다른 사람이 만든 라이브러리를 사용할 경우에는 이런 형태의 형변환을 쓸 수밖에 없습니다. 선배들의 업보를 물려받게 된 셈입니다.)

익명 사용자의 이미지

아이고 제가 오역을 했네요. 바로잡아 주셔서 감사합니다.

익명 사용자의 이미지

Quote:

C에서도 범용 함수 포인터로는 void * 보다는 아무 타입의 함수 포인터를 쓰는게 맞습니다. (C에서 모든 타입의 함수 포인터들은 동일한 내부 표현을 가집니다. 단, 형변환 연산자를 제대로 잘 써줘야 합니다)

그냥 이 글타래만 읽고, 따로 조사하지 않고 질문드립니다.
리턴 타입이 없는 그러니까 리턴형이 void인 함수의 함수 포인터 형은 void * 로 알고 있습니다. C에서 모든 함수 포인터들의 내부 표현이 동일하다면, 그리고 범용 함수 포인터로 아무 타입의 함수 포인터를 쓰는 것이 맞다면, 왜 void *형은 예외로 하고 있는 것인지 궁금합니다.
왠지 void란 표현에 제가 알고 있는 것 이상의 무엇이 있을 거 같은 느낌도 드는데, 한번 질문드려봅니다.
klara의 이미지

리턴타입이 void인 함수 포인터는 void (*)(인자타입)입니다. 함수 포인터의 선언방법을 찾아보세요. 인자도 없는 경우에는 void(*)(void)가 함수 포인터의 타입입니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.