c 언어에서 구조체 포인터 변수의 맴버 접근에 대해서...
글쓴이: rlj1202 / 작성시간: 수, 2016/07/13 - 2:33오후
일반적인 포인터 변수같으면 Test라는 구조체가 있을때 Test * test; 라는 변수가 있다고 하면 맴버를 접근할 때 test->맴버 로 접근하고, 이는 곧 (*test).맴버 와 같잖아요?
그런데 제가 jni 에서 JavaVM* vm 이라는 포인터에서 함수를 접근할때
(**vm).함수명;
으로 접근해야 실행할 수 있더군요.
분명 vm은 이중으로 포인터를 사용하고 있는것 같지 않은데...
그래서 생각해 보건데, 다음과 같은 일이 가능한가요?
int a = 123;
int *b = &a;
int c = b;
printf("%d", *c);
이런식으로 일반 변수에 주소값을 담고 그 변수에 주소값 참조명령을 사용할 수 있는건가요?
자바를 하다가 c언어를 하니 포인터가 제일 어렵네요.
Forums:
C언어는 이식성을 갖춘 언어입니다. 대표적인 활용
C언어는 이식성을 갖춘 언어입니다. 대표적인 활용 사례가 바로 리눅스 커널이 되겠죠.
자바를 하셨다고 하니, 자바 역시도 그런 언어라는 건 아실 테죠. 하지만 두 언어가 이식성을 갖추는 방식은 제법 다릅니다.
자바는 JVM의 지원에 힘입어, 어떤 환경에서든 상당히 일관성 있고 프로그래머에게 친숙한 프로그래밍 환경을 제공해 줍니다.
C언어는 어떤 환경이든 포용할 수 있는 유연한 표준을 제공하며, 프로그래머에게 최소한의 보장을 해 줄 뿐 기반 환경을 그대로 노출시킵니다.
이러한 상반되는 전략은, 두 언어에서
int
를 어떻게 정의하고 있는지만 봐도 확연히 드러나죠.-Java-
The values of the integral types are integers in the following ranges:
For int, from -2147483648 to 2147483647, inclusive.
( https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.2.1 )
-C-
5.2.4.2 Numerical limits
1 An implementation is required to document all the limits specified in this subclause, which are specified in the headers and . Additional limits are specified in .
Forward references: integer types (7.18).
5.2.4.2.1 Sizes of integer types
1 The values given below shall be replaced by constant expressions suitable for use in #if preprocessing directives. Moreover, except for CHAR_BIT and MB_LEN_MAX, the following shall be replaced by expressions that have the same type as would an expression that is an object of the corresponding type converted according to the integer promotions. Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign.
(중략)
— minimum value for an object of type int
INT_MIN -32767 // -(2^15 - 1)
— maximum value for an object of type int
INT_MAX +32767 // 2^15 - 1
( C99 표준. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf 에서 최종 드래프트를 볼 수 있습니다. )
자바는
int
의 표현 범위를-2147483648
~2147483647
으로 단정짓고 있습니다. 어떤 머신에서든 이러한int
를 쓸 수 있게 보장하는 것은 JVM의 몫입니다.C는
int
의 표현 범위는 구현 환경에 맡기되(implementation-defined) 그 상한과 하한을 각각INT_MAX
와INT_MIN
과 같이 limits.h 헤더에 상수로 정의하여 프로그래머가 참조할 수 있도록 하고 있습니다. 덧붙여,int
가 최소한-32767
~+32767
을 표현할 수 있어야 한다는 최소 요구조건도 담고 있지요. C 컴파일러를 만드는 사람은 이러한 요구조건을 만족하는 어떠한 방법으로도int
를 구현할 수 있습니다. (보통 머신의 1워드를 사용하지요.) 또한 이식성 있는 C 프로그램을 만들 때 가정할 수 있는 것도 오직 그뿐입니다.쓸데없이 장황한 배경지식을 왜 늘어놓는가 하면, 비록 32비트 x86에서
int
와 포인터가 동일한 크기(4바이트)를 가지는 등 서로 표현능력이 같다고 해도, 일반적으로는 그러한 가정을 할 수 없다는 말씀입니다. 멀리 가지 않아도 64비트 x86_64만 해도 그렇습니다. 보통int
는 여전히 4바이트이지만, 포인터는 8바이트가 되지요. 그 경우에 포인터를int
로 변환하면 값이 손실되는 게 당연한 일이겠죠.짧게 줄이면, 아래 코드는 이식성에 문제가 있는 위험한 코드입니다.
한 가지 더. 역참조 단항 연산자
*
는 포인터에 대해서만 적용 가능합니다. 그래서 설령 포인터를int
로 변환할 때 손실이 일어나지 않는다 해도 거기서 문제가 됩니다.자바에서 C언어로 왔을 때 포인터에서 어려움을 느끼고 계신다는 것은
1) 위에 언급한 C언어와 자바의 철학 차이를 이해하지 못하고 계시거나
2) C언어와 밀접하게 연관되어 있는 "컴퓨터 구조"에 대한 이해가 부족하거나
3) 혹은 둘 다거나
인 상황이니, C언어의 문법 같은 것에만 너무 몰두하지 마시고 다방면으로 공부하시는 것이 바람직합니다.
그럼 제가 맨 처음에 의문을 가졌던 JavaVM*
그럼 제가 맨 처음에 의문을 가졌던 JavaVM* vm 의 함수를 참조할때 (**vm).함수; 로 해야 했던것은 어떤 연유로 그런것 인가요?
예상하기로는
JavaVM
타입 자체가 사실 포인터 타입이기 때문이겠지요. 그래야만*vm
(JavaVM
타입)이 포인터일 테니까요.저는 jni는 잘 모릅니다만, 문서를 조금 찾아보니...
=== 발췌( https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html ) ===
The JavaVM type is a pointer to the Invocation API function table. The following code example shows this function table.
(후략)
======
역시나.
JavaVM
타입은 사실const struct JNIInvokeInterface *
이었군요.typedef
을 이런 식으로 쓰는 건 사실 초보자들을 다소 혼란스럽게 만드는 경향이 있는데, 뭐 별 거 없습니다.요약하자면,
JavaVM* vm
와 같이vm
을 선언하였다면, 사실const struct JNIInvokeInterface ** vm
와 같이 선언하신 셈이란 겁니다. 모르는 사이 이중 포인터를 사용하고 계셨던 셈이죠.헉 밑의 예제 코드 쓰다가 실수했네요.
-_-;;; 왜 포인터가 아니라 참조자를 썼지; 잠깐 졸았나봅니다.
const 는 상수인데, 포인터 변수 자체도
const 는 상수인데, 포인터 변수 자체도 상수이지만 그 값을 참조하여 값을 바꾸는것 또한 금지 되는군요!
JavaVM 같은 경우 그 타입 자체로 포인터 변수인 것 이구요.
감사합니다!
헉;; 아닙니다.
일단 코드부터 보여드리죠.
요점은, 포인터에
const
를 적용할 때 "가리키는 것이 상수이거나", "포인터 자체가 상수이거나", 혹은 "둘 다거나"의 세 가지 경우가 있다는 말씀입니다.어느 게 어느 경우인지 정확히 파악하려면, 휴먼 컴파일러가 되어야 합니다. 말인즉슨 C언어의 복잡하고 재귀적인 선언문 문법을 정확히 이해하고 적용할 줄 알아야 된다는 뜻이죠.
... 그런데, 솔직히 말씀드려서, C언어의 선언문 문법은 진짜 끔찍하게 복잡해요. 뭐 그만큼 유연하다는 의미이기도 한데, 언어 표준의 EBNF로 작성된 문법 정의를 읽는 데 어느 정도 익숙한 사람에게도 짜증을 유발시킬 정도입니다.
지금 저도 옆에 띄워놓고 보고 있는데, 대략 십여 페이지 정도의 해당 부분을 전부 발췌하지 않고는 도저히 설명을 못 드릴 거 같네요.
꼭 읽어보시겠다면 앞서 드린 드래프트( http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf ) 에서 6.7 Declarations, 6.7.3 Type qualifiers, 6.7.5 Declarators 위주로 읽어보세요.
그냥 간단히 (다소 모호하게) 정리해드리죠.
*
오른쪽에const
가 있으면 그건 포인터 자체가 상수, 즉 포인터가 다른 걸 가리키게 만들 수 없다는 의미일 겁니다.*
왼쪽에 있는const
는 포인터가 "가리키는 것"이 상수, 즉 포인터가 가리키고 있는 객체의 값을 변경할 수 없다는 의미일 겁니다.이 정도 설명만으로도 많은 경우가 명확해질 거에요. 그리고 충분히 숙련되시기 전까지는, 이 설명 가지고는 이해할 수 없는 선언문은 가급적 안 만드시는 게 좋습니다. 아무리
const
가 쓸 수 있는 한 많이 쓰는 게 좋다지만, 알고 써야죠.아 요즘 왜 자꾸 정신줄을 놓지 -_-;;;
일부러 에러를 포함시키는 예제코드를 작성할 땐 컴파일러한테 검수받기가 무척 귀찮아서 보통 거르는데, 그러다보니 별 해괴한 실수를 고스란히 남기게 되는군요.
피곤해서 그런 것도 있는 거 같네요. 이거 올리고 잘 겁니다. -_-;; 다른 문제가 있더라도 적당히 직접 보완해가면서 읽어 주시길.
헠....디테일한 설명 감사합니다.
헠....디테일한 설명 감사합니다.
댓글 달기