LONG_PTR과 비슷한 스타일의 리눅스, 유닉스용 데이터 타입은?

bizzare의 이미지

윈도우 환경에서

LONG_PTR과 같은 데이터 타입이 있지요.

32비트 환경으로 빌드하면 4바이트 변수,
64비트 환경으로 빌드하면 8바이트 변수가 되는 데이터 타입입니다.

리눅스나 유닉스에서도 이런 데이터 타입을 정의해서 쓰고 싶은데,
어떻게 하는 것이 좋을까요?

#if defined(_WIN64)
typedef __int64 INT_PTR, *PINT_PTR;
#else
typedef _W64 int INT_PTR, *PINT_PTR;

윈도우는 위와 같이 int와 __int64를 이용하고 있습니다만,

조언 부탁드립니다.

tj의 이미지

[u]intptr_t 예요.

Hyun의 이미지

"long" 이 32비트에선 4바이트, 64비트에선 8바이트 아닌가요??
글고 포인터는 무조건 32에선 4, 64에선 8바이트 아닌가요??

tj의 이미지

32bit에선 다 sizeof(int) == sizeof(long) == sizeof(void *) 라고 생각하면 되구요. 유닉스 계열은 64bit에서 다 ILP64아니면 LP64니까 long/unsigned long 써도 되는데, P64 인 플랫폼이 없는 건 아니어서 (윈도우가 그렇죠 아마?), 표준상으론 [u]intptr_t 쓰는게 정확하긴 합니다만, [u]long 써서 문제가 될 경우는 앞으로 30년 정도는 없지 않을까 싶어요.

전웅의 이미지

> 윈도우 환경에서
>
> LONG_PTR과 같은 데이터 타입이 있지요.
>
> 32비트 환경으로 빌드하면 4바이트 변수,
> 64비트 환경으로 빌드하면 8바이트 변수가 되는 데이터 타입입니다.
>
> 리눅스나 유닉스에서도 이런 데이터 타입을 정의해서 쓰고 싶은데,
> 어떻게 하는 것이 좋을까요?
>
> #if defined(_WIN64)
> typedef __int64 INT_PTR, *PINT_PTR;
> #else
> typedef _W64 int INT_PTR, *PINT_PTR;
>
> 윈도우는 위와 같이 int와 __int64를 이용하고 있습니다만,
>
> 조언 부탁드립니다.
>

LONG_PTR 의 목적은 정보 손실 없이 "포인터-정수 간 변환"을 수행하기
위한 것입니다.

우선 드리고 싶은 말씀은 정보 손실 없이 포인터-정수 간 변환을 수행할
수 있는 정수형이 "모든" 환경에서 존재하는 것은 아니라는 것입니다.
드물기는 하지만, 매우 제한적인 환경 (예를 들면, word-addressing
machine) 에서 표준 요구 수용을 위해 void * 나 char * 같은 포인터가
불가피하게 커지는 경우가 있습니다. 이 경우 그러한 포인터가 담는 정보를
손실 없이 해당 환경의 가장 큰 정수형으로 옮기는 것이 불가능할 수도
있습니다.

따라서 말씀하신 목적(정확히는 void * 와 정수 사이의 변환)으로 C99 에서
제공하는 정수형인 uintptr_t/intptr_t 는 강제로 요구되는 type 이
아닙니다. 포인터-정수 변환을 위한 정수형이 아예 제공되지 않던 C90 에
비하면 이식성에 큰 보장을 얻은 셈이지만, type 의 존재 자체가 강제가
아니라는 사실은 기억하셔야 합니다.

따라서 [u]intptr_t 와 함께 제공되는 매크로 (INTPTR_MIN, INTPTR_MAX,
UINTPTR_MAX) 의 정의 여부를 전처리 단계에서 확인해야 합니다.

#include <stdint.h>
 
#ifndef UINTPTR_MAX
#error This code requires uintptr_t.
#endif

C90 환경의 경우에는 [u]intptr_t 가 따로 제공되지 않기 때문에 (forward
compatibility 를 위해 동일한 이름으로) 직접 정의해 사용하실 수도
있습니다. 단, C99 가 주어진 환경에서 가장 큰 정수형을 위한 이름
([u]intmax_t)을 제공하는 반면, C90 에서는 unsigned long 보다 더 큰
정수형이 확장으로 제공되더라도 그에 접근할 수 있는 이식성 있는 방법이
없기 때문에 어쩔 수 없이 코드가 특정 환경에 의존하거나 아니면 위험을
감수하고 unsigned long 에 의존하는 방법 뿐이 없습니다.

> 32bit에선 다 sizeof(int) == sizeof(long) == sizeof(void *) 라고 생각하면 되구요.
> 유닉스 계열은 64bit에서 다 ILP64아니면 LP64니까 long/unsigned long 써도 되는데,
> P64 인 플랫폼이 없는 건 아니어서 (윈도우가 그렇죠 아마?), 표준상으론 [u]intptr_t
> 쓰는게 정확하긴 합니다만, [u]long 써서 문제가 될 경우는 앞으로 30년 정도는 없지
> 않을까 싶어요.

세상의 모든 개발/실행 환경이 유닉스 계열과 Windows 계열 둘로만 나뉘는
것은 아닙니다. 근거 없이 "32-bit에선 다" 라고 말씀하셔서 드리는 말씀
입니다만, 32-bit 환경이라고 [unsigned] int 와 [unsigned] long 이
정확히 32 bits 크기로 제공되도록 강제되는 것은 아닙니다. 예를 들어,
MC68000 이라는 놈은 32-bit CPU 이지만 16-bit ALU 를 가지고 있어 16-bit
연산이 더 효율적입니다. 따라서 16-bit int 형이 선택되기도 했습니다.
더구나 sizeof(void *) > sizeof(uintmax_t) 인 환경도 있습니다. 또한,
[u]intptr_t 가 가능한 상황에서 근거 없는 "30년" 미신으로 ([u]intmax_t
도 아닌) [unsigned] long 을 선택할 이유는 없습니다.

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

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

tj의 이미지

그렇게까지 따지는게 의미가 있다고 생각하지 않습니다. 플랫폼간 다른 점을 고려하면서 프로그래밍하는 것도 중요하지만 어떤 규칙이든 교조적으로 따지면 득보단 실이 많습니다. 이식성 역시 다른 것들과 함께 트레이드오프시켜야할 요소 중 하나이고, 백퍼센트 커버하려는 것보단 합리적인 수준에서 타협하고 예외는 예외로 남겨두는게 낫다고 생각합니다. "현재" 제조되는 32bit cpu중 ALU가 16bit이어서 int가 16bit인 경우가 있는지요?

[u]long 경우는 좀 더 회색 영역에 있긴 합니다만, LP64가 다른 방식에 비해 우월하다는 점이 일반적으로 인정되고 있고, 이미 선택된 상황에서 128bit addressing이 필요해지는 경우가 아니라면 sizeof(long) != sizeof(void *)가 될 가능성이 매우 낮습니다. 30년은 "미신" 이라기 보다는 좀 유머스러운 예측 정도로 하고 싶은데요. 하드웨어, 오에스 어느쪽에서 따져도 주류시장에서 16bit->32bit 전환은 넓게 10~20년 걸렸습니다. 매년 착실하게 두배 근처로 늘어나고 있으니 당연한 결과고, 지금 컴퓨팅 환경이 유지되면서 발전한다면 (설마) 30년후쯤에는 long을 64bit으로 할지 128bit으로 할지 고민하고 있을 수도 있겠지요.

표준이나 언어의 여러면을 따질 때 엄밀함도 분명히 중요합니다만, 완전은 좋음의 적이라고도 하잖아요.

익명 사용자의 이미지

전웅의 이미지

> 아니 왜 잘팔리고 있는 CPU를 가지고...
>
> http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MC68000&n...
>

좋은 정보 감사합니다.

5년전 MC68000 이야기가 나왔을 때 대부분 "기억"만 하고 당시 "사용"하고
있다는 사람이 단 한 명이라 90년대 단종을 예상했었는데 지금까지도
명맥을 유지하고 있군요.

역시 machine 의 수명은 생각보다 오래가고, 프로그램의 수명은 machine
보다 오래가며, data 의 수명은 프로그램보다 오래 간다는 교훈을 다시
한번 확인하게 됩니다.

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

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

전웅의 이미지

아마도 이 글이 KLDP 에 올리는 마지막 글이 될 듯 싶습니다.

> 그렇게까지 따지는게 의미가 있다고 생각하지 않습니다. 플랫폼간 다른 점을 고려하면서
> 프로그래밍하는 것도 중요하지만 어떤 규칙이든 교조적으로 따지면 득보단 실이 많습니다.

교조적이란 "30년" 미신을 표현하는 용어이지, 표준 수준의 이식성을
표현하는 용어가 아닙니다.

> 이식성 역시 다른 것들과 함께 트레이드오프시켜야할 요소 중 하나이고, 백퍼센트 커버하려는
> 것보단 합리적인 수준에서 타협하고 예외는 예외로 남겨두는게 낫다고 생각합니다. "현재"
> 제조되는 32bit cpu중 ALU가 16bit이어서 int가 16bit인 경우가 있는지요?
>

Y2K 문제를 경험할 때, 현재 "제조"되는 컴퓨터나 현재 "제조"되고 있는
정보에 그 문제가 있었습니까, 아니면 오래 전제 제조되어 현재 "사용"
중인 것들에 문제가 있었습니까?

약간의 고민만으로 얻을 수 있는 일반성을 포기하고, 이식성 문제를 회피
하고자 하는 사람들이 주장하는 전형적 절차에 불과합니다:

- 그런 것이 어디 있느냐?
- 현재 "제조"되느냐?
- 얼마나 사용되느냐?

문제는 현재 "제조"되느냐가 아니라 현재 "사용"되느냐 입니다. 16-bit
ALU 를 쓰는 68000, 68008, 6801x 등은 극소수에 의해 사용되고
있을지라도 (5년전 68000 을 사용하고 있다는 사람을 본 적이 있습니다),
해당 환경에서 사용되던 컴파일러가 추후 출시된 32-bit ALU 를 쓰는 환경
에서도 사용되고 있습니다. 심지어 어떤 컴파일러는 두 환경을 모두
고려해 int 의 비트수를 옵션을 통해 설정할 수 있도록 하기도 합니다 -
참으로 참한 컴파일러가 아닐 수 없습니다.

32-bit ALU 를 사용하는 68xxx 시리즈가 나오면 그 이전 시리즈에서 사용
되던 프로그램, 자료 등은 모두 폐기하고 새 술은 새 부대에 담는다는
사고로 처음부터 새로 시작하는 것이 우리 동네 스타일이었나요?
안타깝게도 그러지 못한 것이 현실입니다.

표준이 완전한 일반성을 제공하는 상황에서, 캐스트하는 type 좀 변경
하거나, (제가 보인 예에서처럼) 조건부 컴파일 3줄 넣으면 가장 일반적인
이식성을 확보할 수 있음에도 불구하고, 굳이 애써 이를 무시하며 "대부분
괜찮을 것이다" 라는 가정에 기대는 이유를 이해하기 어렵습니다.

이식성이 엄청난 양의 코드 수정이나 프로그래머에게 지나친 부담이 되는
경우에는 *반드시* trade-off 의 대상이 되어야 합니다. 하지만, 지금
논의되고 있는 상황은 그런 상황이 아닙니다. 특히나 추측컨데 원 질문자
는 프로그램 포팅의 첫 단계이므로 신중하게 설계만 한다면 앞으로 상당한
기간 우려먹을 수 있는 코드를 얻을 수도 있습니다.

* 어쩌다보니 마치 님께서 [u]intptr_t 를 두고 [unsigned] long 으로의
변환을 강조한 것처럼 이야기가 진행되고 있습니다만, 님이나 저나 그렇지
않다는 것은 잘 알고 있습니다. 님께서 "32bit에선 다 sizeof(int) ==
sizeof(long) == sizeof(void *)" 라고 언급하셨고, 이 문장을 "32bit에선
다" 대신 "널리 쓰이는 32bit 환경에선" 이라고 수정할 경우 참이 됩니다.
하지만, 분명 "32bit에선 다" 로 둘 경우 거짓임은 분명하고 전 그 근거를
보인 것입니다. 혹시나 이야기가 진행되며 주제가 흐릿해지거나 서로의
의견이 왜곡될 수 있기에 언급해 둡니다.

> [u]long 경우는 좀 더 회색 영역에 있긴 합니다만, LP64가 다른 방식에 비해 우월하다는 점이
> 일반적으로 인정되고 있고, 이미 선택된 상황에서 128bit addressing이 필요해지는 경우가
> 아니라면 sizeof(long) != sizeof(void *)가 될 가능성이 매우 낮습니다.
>

64-bit 환경만 따진다면 사실상 현재 Win64 가 유일하게 P64 를 채용하고
있는 것으로 알려져 있습니다. 하지만, 그 외의 환경까지 수용한다면
sizeof(long) 과 sizeof(void *) 사이에 "필연적인" 연관성이 제공되지
않습니다.

1. C99 환경이면
1-1. UINTPTR_MAX 가 정의되어 있으면 uintptr_t 사용
1-2. UINTPTR_MAX 가 정의되어 있지 않으면 #error 처리
2. C90 환경이면
2-1. 확장 통해 최대 정수형 제공하는 환경에 대해 조건부 컴파일 처리
2-2. 그 외의 환경에 어쩔 수 없이 unsigned long 사용

이 간단한 구조(10여 줄의 조건부 컴파일 구문)만 추가해도 충분한 것을
처음부터 이식성을 포기하고 [unsigned] long 으로의 변환에 의존할
이유는 없습니다. 또한 그런 이식성 포기의 기반에 "sizeof(long) ==
sizeof(void *)" 라는 생각이 깔려 있다면 더더욱 위험합니다. "어떤
정수형이 void * 를 정보 손실 없이 표현할 수 있다"는 생각이 항상 참이
아니며 위험할 수도 있다는 사실은 표준 뿐 아니라 여러 C 언어 책에서
상당히 강조하고 있는 내용이기도 합니다. 만약 그와 같은 가정이 완전히
의존할 수 있는 것이었다면 표준이 [u]intptr_t 를 추가할 이유도, 더구나
이를 optional type 으로 만들 이유도 없었을 것입니다.

> 표준이나 언어의 여러면을 따질 때 엄밀함도 분명히 중요합니다만, 완전은 좋음의 적이라고도 하잖아요.
>

"좋음은 완전의 적"이 세간에 떠도는 말입니다. [unsigned] long 으로의
변환은 확실히 좋습니다. 하지만 이 경우는 코드 몇 줄 추가로 쉽게
완전함을 얻을 수 있는 경우입니다.

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

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

익명 사용자의 이미지

Quote:
아마도 이 글이 KLDP 에 올리는 마지막 글이 될 듯 싶습니다.

이제 얄팍한 지식으로 떠드는 애들은 기뻐하겠군요. -_-;
tj의 이미지

거참, 꼭 이런식으로 가야됩니까?

tj의 이미지

* 16bit ALU CPU가 여전히 제조되고 있군요. 재미있습니다만 실질적으로 얼마나 의미가 있는지 모르겠습니다. 형의 크기에 대한 가정은 프로그램 전반에 존재할 수 밖에 없습니다. 현 상황에서 특수한 경우가 아니면 int 16bit 일 때 제대로 돌아갈 소스도 많지 않고, 포팅 시 int와 포인터 크기가 달라지는 건 풀기 쉬운 편에 속합니다. 컴파일러가 알려줄 수도 있구요. [u]intptr_t 논의를 떠나서 예외상황이라는데 동의하지 않으시는지요?

* 글 제목이 "LONG_PTR과 비슷한 스타일의 리눅스, 유닉스용 데이터 타입은?" 입니다. "32bit에선 ~다"를 "32bit 유닉스에선 ~다"로 바꾸시는 게 제 의도와 더 잘 맞습니다.

* C 표준은 유닉스보다 훨씬 넓은 영역을 정확하게 표현해야합니다. 당연히 유닉스 환경의 일반적인 프로그래밍과 차이가 있을 수 밖에 없습니다. 여태까지 (일반적으로 쓰이는) 모든 64bit 유닉스가 ILP64아니면 LP64 모델이고, int 크기는 ABI의 일부이기 때문에 유저스페이스에서 마음대로 다르게 쓸 수 있는 것도 아닙니다. 64bit이 유지되는 한 앞으로 변할 가능성도 매우 낮구요. 그런 이유로 (일반적으로 쓰이는) 유닉스 환경에서 sizeof(long) == sizeof(void *) 가정을 많이 하는 편이고, 저는 유닉스 환경에서 그 정도 가정은 받아들일만 하다고 생각합니다. PRI/SCN 메크로들 보기 피곤하기도 하구요. 희망사항일지 모르겠지만 sizeof(long) == sizeof(void *) 가정은 POSIX에 권고로 들어가도 크게 이상하지 않으리라 생각합니다.

* 첫 답변에서 적었듯 [u]intptr_t가 정답입니다. 동의합니다. 하지만 실제로 [u]intptr_t를 쓰는게 얼마나 이식성에 도움이 될지엔 의구심이 있습니다.

익명 사용자의 이미지

실질적으로 볼때,
sizeof(long) != sizeof(void *) 일수도 있다 라는 가정을 받아들이는 것이
실제 코드를 작성하는데 있어서
과연 그렇게나 어렵고 힘든 일인가 하는 의문이 듭니다.

어차피 일정 규모 이상의 코드가 되면
코드에 필요한 각각의 데이터형을 추상적인 의미와 용도에 따라서
재정의해서 쓰는건 피할 수 없는 일이고,
이것은 #ifdef를 통한 조건부 컴파일로 아주 간단하게 해결할 수 있는 일인데,
그럼에도 불구하고 ulong에 그렇게나 집착해야 합니까?

게다가 그 이전에 정수형과 포인터형 사이의 무분별한 형변환과 혼용은
결코 바람직한 일이 아닐텐데요.
포인터형과 특정 정수형의 크기가 같다는 가정을 사용한다는거 자체가
이미 설계가 잘못된 코드라고 생각하지 않으십니까?

님의 고집이 실질적으로 얼마나 더 의미가 있는지 모르겠군요.

tj의 이미지

포인터 <-> 정수형 변환은 자주 하는 일은 아닙니다만, 특정 프로그래밍 영역에선 그렇게까지 드물지 않습니다. [u]intptr_t 가 표준이고 정답 맞습니다 (답변도 그렇게 달았잖아요). 제가 주장하는 바는 (일반적으로 쓰이는) 32/64bit 유닉스 시스템에서 sizeof(long) == sizeof(void *)이 성립한다 와 [u]intptr_t이 과연 좋은 해답인지 의구심이 있다 입니다. 예를 들면 "uintptr_t이 맞긴한데, 좀 지저분해. 아 모르겠다. 그냥 웬만하면 ulong 써도 큰 문제는 없어" 정도 입니다.

표준화는 일방적인 과정이 아닙니다. C99에서 새로이 채택된 많은 추가사항들이 다른 컴파일러에서 비표준 확장으로 지원되던 기능을 표준으로 인정한 것이기도 하구요. [u]intptr_t 가 표준에 맞는 방법이긴 합니다만, [u]long의 사용에 비해 확실히 낫다고 하기엔 쓰다보면 자잘하게 애로사항들이 있습니다. 그래서 적어도 유닉스에선 sizeof(long) == sizeof(void *) 가정은 심심치않게 쓰이는 편이고, [u]long의 크기를 그렇게 정하는게 우월한 방법이라는게 중론입니다. 표준에 들어가지 않을진 몰라도, 가정이 틀려질 가능성은 매우 낮습니다.

쓰레드가 계속 공격적이 되는데 좀 살살 가면 안되겠습니까?

tj의 이미지

아 쓰다 보니 생각이 났는데요. PRI/SCN 메크로 외에도 (c99 아닌데서 이것도 추가해줘야죠), 주소나 주소마스크 상수뒤에 타입 꼬리를 뭘로 붙일지도 명확하지 않습니다. :-(

전웅의 이미지

충분히 발전적인 쓰래드라 이 쓰래드는 마쳐야 겠다는 생각에 글
남깁니다.

> * 16bit ALU CPU가 여전히 제조되고 있군요. 재미있습니다만 실질적으로 얼마나 의미가
> 있는지 모르겠습니다. 형의 크기에 대한 가정은 프로그램 전반에 존재할 수 밖에 없습니다.
> 현 상황에서 특수한 경우가 아니면 int 16bit 일 때 제대로 돌아갈 소스도 많지 않고, 포팅
> 시 int와 포인터 크기가 달라지는 건 풀기 쉬운 편에 속합니다. 컴파일러가 알려줄 수도
> 있구요. [u]intptr_t 논의를 떠나서 예외상황이라는데 동의하지 않으시는지요?
>

이야기가 너무 많이 왔습니다. 부분적으로 이어진 논의는 "32bit 에선 다"
때문에 시작된 것입니다. 그것이 참이 아님은 분명히 보인 바 있고, 그
이후 정수형의 이식성과 관련되 문제에 저는 관심 없습니다. 정수형의
크기에 대한 가정이 프로그램 "전반"에 있을 수 밖에 없다고
말씀하셨는데, 실력 있는 사람은 그 가정을 최소화할 것이고, 그렇지 않은
사람은 그러지 못하겠지요.

> * 글 제목이 "LONG_PTR과 비슷한 스타일의 리눅스, 유닉스용 데이터 타입은?" 입니다.
> "32bit에선 ~다"를 "32bit 유닉스에선 ~다"로 바꾸시는 게 제 의도와 더 잘 맞습니다.
>

MC68000 에도 Unix 가 올라간 적이 있습니다. 왜 위험성을 떠 안으시면서
"다" 라는 표현을 고집하시는지요? "우리가 접할 수 있는 대부분의 32-bit
환경에선" 이라고만 표현해도 전달하시려는 바를 모두 전달할 수
있습니다.

> * C 표준은 유닉스보다 훨씬 넓은 영역을 정확하게 표현해야합니다. 당연히 유닉스 환경의
> 일반적인 프로그래밍과 차이가 있을 수 밖에 없습니다.

표준과 UNIX 환경의 일반적 프로그래밍에 구분선을 그으려 하시는 것으로
보입니다. UNIX 환경은 표준을 매우 잘 따르는 환경으로 유명합니다. UNIX
가 표준을 따르는 것이고, 반대로 표준은 UNIX 를 아우르는 것입니다.
쉽게 이야기해, 표준을 준수하면 모든 UNIX 에도 동일한 결과를 얻을 수
있음을 뜻합니다.

> 여태까지 (일반적으로 쓰이는) 모든
> 64bit 유닉스가 ILP64아니면 LP64 모델이고, int 크기는 ABI의 일부이기 때문에 유저스페이스에서
> 마음대로 다르게 쓸 수 있는 것도 아닙니다. 64bit이 유지되는 한 앞으로 변할 가능성도 매우 낮구요.
> 그런 이유로 (일반적으로 쓰이는) 유닉스 환경에서 sizeof(long) == sizeof(void *) 가정을 많이
> 하는 편이고, 저는 유닉스 환경에서 그 정도 가정은 받아들일만 하다고 생각합니다.

그럼 그렇게 가정하시고 코딩하시면 됩니다. 그로 인해 안게 되는 위험
부담은 님과 님을 고용한 회사가 지는 것이구요. 제가 드리고 싶은 말씀은
님이 그런 가정을 포기해야 한다는 것이 아니라, 아주 간단한 노력
만으로도 그런 가정에 의존하지 않고 안전한 코드를 작성할 수 있다는
것입니다. 즉, sizeof(long) == sizeof(void *) 의 가정이 불가피하게
필요한 것은 아니라는 것입니다.

> PRI/SCN 메크로들 보기 피곤하기도 하구요.

전 오히려 나중에 이식성 없게 작성된 부분들로 인해 발생할 문제를 수정
해야 할 생각을 하니 더 피곤해 지는군요. 님께서 30년을 보장하셨으니 한
31년 쯤 후에는 수정이 필요할테니까요. :-)

지금 사소한 비용을 들여 추후 큰 비용을 막을 수 있는 경우입니다. 그
나중의 비용이 실제 발생할 확률이 낮다구요? 누가 미래를 알겠습니까?
물론 선택은 본인이 하고 책임은 본인 (혹은 종종 억울한 후임) 이 지는
것입니다.

> 희망사항일지 모르겠지만 sizeof(long) == sizeof(void *) 가정은 POSIX에 권고로 들어가도
> 크게 이상하지 않으리라 생각합니다.

그럴리 없습니다. 정치적으로 보자면 POSIX 에 영향을 미치는 많은 사람
들이 C 표준화에도 관여하고 있고, 저보다 더 까탈스러운 사람들입니다.
POSIX 에 그런 허무맹랑한 요구를 넣는 것을 보고 있을 사람들이
아닙니다.

> * 첫 답변에서 적었듯 [u]intptr_t가 정답입니다. 동의합니다. 하지만 실제로 [u]intptr_t를
> 쓰는게 얼마나 이식성에 도움이 될지엔 의구심이 있습니다.

표준이 약속한 [u]intptr_t 에 대한 의구심은 표준 전체의 영향력에 대한
의구심으로 보일 수 있습니다. [u]intptr_t 에 대한 의구심은 가지고
계시면서 C 언어의 다른 구조가 갖는 의미나 이식성에 대한 의구심은 왜
가지고 계시지 않은지 궁금하군요. C 표준이 다른 부분은 다른 표준에
정의를 맡기고 [u]intptr_t 만 정의해주고 있는 것도 아닌데 말입니다.

표준의 강제력은 시장의 동의에 의해 형성되며, 그렇게 형성된 표준의
힘은 생각보다 강력합니다.

> 아 쓰다 보니 생각이 났는데요. PRI/SCN 메크로 외에도 (c99 아닌데서 이것도 추가해줘야죠),
> 주소나 주소마스크 상수뒤에 타입 꼬리를 뭘로 붙일지도 명확하지 않습니다. :-(
>

억지스럽게 만들어 낸 문제입니다. 어차피 [u]intptr_t 의 실제 type 에
대한 가정 없이는 주소 마스크 상수를 구성할 수 없거나, 혹은 이식성
있는 방법으로 주소 마스크 상수를 구성하고자 하는 경우 ([u]intptr_t)
캐스트로 형 변환 후 연산을 적용할 수 있습니다.

표준이 typedef 를 통해 제공하는 모든 type 에 대해 상수 postfix 를
위한 매크로를 제공하고 있는 것은 아님에 유의하시기 바랍니다. 필요성에
대한 충분한 검토가 있은 후에 추가된 매크로들입니다.

> [...] 제가 주장하는 바는 (일반적으로 쓰이는) 32/64bit 유닉스 시스템에서
> sizeof(long) == sizeof(void *)이 성립한다 와 [u]intptr_t이 과연 좋은 해답인지 의구심이
> 있다 입니다. 예를 들면 "uintptr_t이 맞긴한데, 좀 지저분해. 아 모르겠다. 그냥 웬만하면
> ulong 써도 큰 문제는 없어" 정도 입니다.

"지저분해"는 좀... --;

많은 프로그램이 sizeof(long) >= sizeof(void *) 에 의존하고 있으며,
이 가정이 깨질 경우 그와 같은 프로그램이 오동작하게 될 것이라는
사실은 분명합니다 - 따라서 그런 프로그램을 많이 보유하고 있다면 하위
호환성을 걱정하는 implementation 이 쉽게 깨지 못할 가정입니다.

하지만 많은 프로그램이 그렇게 하고 있다고 해서 그것이 올바르고 항상
보장받는 것은 아닙니다. 그렇게 많은 프로그램이 기대고 있는 가정임에도
정수-포인터 간의 변환은 이식성이 없는 구조임이 재차 확인되어 왔습니다.
즉, 님께서 프로그램을 작성하는 환경을 구현하는 사람들조차 정수-포인터
간 변환의 이식성을 인정하지 않으며, 만약 가능한 경우에 한해서라도
이식성 확보를 할 수 있도록 [u]intptr_t 를 제공하기로 "약속"을 한 것
입니다.

그 약속을 모르는 상태에서 [unsigned] long 을 사용하신다면 어쩔 수
없겠지만, 알고 계시면서도 [u]intptr_t 를 애써 무시하시는 이유를
저로서는 이해하기 어렵다는 뜻입니다.

[u]intptr_t 의 사용이 unsigned long 에 비해서 그토록 어려울까요? 기존
코드를 모두 고치는 문제라면 어려울 수 있겠지만, 새로 시작하는
프로젝트에 적용하기도 어려울까요? 제게는 직접 해보지 않으신 상태에서
익숙한 것에 머물러 있으려 하시는 것으로 밖에 보이지 않습니다.

> 표준화는 일방적인 과정이 아닙니다. C99에서 새로이 채택된 많은 추가사항들이 다른 컴파일러
> 에서 비표준 확장으로 지원되던 기능을 표준으로 인정한 것이기도 하구요. [u]intptr_t 가 표준에
> 맞는 방법이긴 합니다만, [u]long의 사용에 비해 확실히 낫다고 하기엔 쓰다보면 자잘하게
> 애로사항들이 있습니다.

말씀해 보시기 바랍니다. 정리해서 csc 논의를 거친 후 살아남는 것들은
표준에 반영될 수 있도록 하겠습니다.

> 그래서 적어도 유닉스에선 sizeof(long) == sizeof(void *) 가정은 심심치않게 쓰이는 편이고,
> [u]long의 크기를 그렇게 정하는게 우월한 방법이라는게 중론입니다. 표준에 들어가지 않을진 몰라도,
> 가정이 틀려질 가능성은 매우 낮습니다.

님이 작성하신 프로그램의 이식성과 가독성을 제한한다는 가정 아래에서
참입니다.

참고로, 보다 치명적인 문제를 일으킬 수 있는 sizeof(char) ==
sizeof(int) 인 환경도 있는데 하물며 sizeof(long) < sizeof(void *) 인
환경이 존재하지 않는 것은 아닙니다. 혹시나 오해가 있을까 하는 우려
에서 남깁니다.

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

tj의 이미지

> 충분히 발전적인 쓰래드라 이 쓰래드는 마쳐야 겠다는 생각에 글
> 남깁니다.

감사합니다.

[--생략--]
> 이야기가 너무 많이 왔습니다. 부분적으로 이어진 논의는 "32bit 에선 다"
> 때문에 시작된 것입니다. 그것이 참이 아님은 분명히 보인 바 있고, 그
> 이후 정수형의 이식성과 관련되 문제에 저는 관심 없습니다. 정수형의
> 크기에 대한 가정이 프로그램 "전반"에 있을 수 밖에 없다고
> 말씀하셨는데, 실력 있는 사람은 그 가정을 최소화할 것이고, 그렇지 않은
> 사람은 그러지 못하겠지요.

크기에 대한 가정이 어느 선까지 적절하고 어느 선을 넘어서면 곤란한지는 쉽게 결정할 수 있는 문제는 아닙니다. 크기에 대한 가정을 _최소_화하는 오버헤드는 높고, 차이가 큰 환경으로의 포팅은 어차피 주의와 작업을 필요로 합니다. 타입들을 다룰 때 컴파일러가 잘못되는 걸 알수있을 정도로 다루면 특정 타입의 사용이 포팅에 주요 장애물이 될 가능성은 낮다고 생각합니다.

>> * 글 제목이 "LONG_PTR과 비슷한 스타일의 리눅스, 유닉스용 데이터 타입은?" 입니다.
>> "32bit에선 ~다"를 "32bit 유닉스에선 ~다"로 바꾸시는 게 제 의도와 더 잘 맞습니다.
>
> MC68000 에도 Unix 가 올라간 적이 있습니다. 왜 위험성을 떠 안으시면서
> "다" 라는 표현을 고집하시는지요? "우리가 접할 수 있는 대부분의 32-bit
> 환경에선" 이라고만 표현해도 전달하시려는 바를 모두 전달할 수
> 있습니다.

극단적으로 예외적인 상황을 언급하는 걸 그다지 좋아하지 않습니다. 실제로 경험할 가능성이 거의 없는 상황을 미리 무섭게 언급하는게 긍정적인 효과가 있다고 생각하지도 않구요. 원 문장은 "다 ~ 라고 생각하시면 되구요" 입니다. "다 ~ 입니다" 가 아니죠. MC68000에 올라간 유닉스는 저 정도 표현으로 커버된다고 하면 안될까요?

>> * C 표준은 유닉스보다 훨씬 넓은 영역을 정확하게 표현해야합니다. 당연히 유닉스 환경의
>> 일반적인 프로그래밍과 차이가 있을 수 밖에 없습니다.
>
> 표준과 UNIX 환경의 일반적 프로그래밍에 구분선을 그으려 하시는 것으로
> 보입니다. UNIX 환경은 표준을 매우 잘 따르는 환경으로 유명합니다. UNIX
> 가 표준을 따르는 것이고, 반대로 표준은 UNIX 를 아우르는 것입니다.
> 쉽게 이야기해, 표준을 준수하면 모든 UNIX 에도 동일한 결과를 얻을 수
> 있음을 뜻합니다.

아니요, C 언어 표준과, POSIX 사이에 구분선을 그으려 한 것 입니다. C 언어 표준이 UNIX를 아우르긴 하지만 UNIX가 C 표준만을 따르진 않습니다. 표준이 있고, (주로 그 안에서 혹은 경계선에 걸쳐서) 따로 관행도 있구요.

>> 여태까지 (일반적으로 쓰이는) 모든
>> 64bit 유닉스가 ILP64아니면 LP64 모델이고, int 크기는 ABI의 일부이기 때문에 유저스페이스에서
>> 마음대로 다르게 쓸 수 있는 것도 아닙니다. 64bit이 유지되는 한 앞으로 변할 가능성도 매우 낮구요.
>> 그런 이유로 (일반적으로 쓰이는) 유닉스 환경에서 sizeof(long) == sizeof(void *) 가정을 많이
>> 하는 편이고, 저는 유닉스 환경에서 그 정도 가정은 받아들일만 하다고 생각합니다.
>
> 그럼 그렇게 가정하시고 코딩하시면 됩니다. 그로 인해 안게 되는 위험
> 부담은 님과 님을 고용한 회사가 지는 것이구요. 제가 드리고 싶은 말씀은
> 님이 그런 가정을 포기해야 한다는 것이 아니라, 아주 간단한 노력
> 만으로도 그런 가정에 의존하지 않고 안전한 코드를 작성할 수 있다는
> 것입니다. 즉, sizeof(long) == sizeof(void *) 의 가정이 불가피하게
> 필요한 것은 아니라는 것입니다.
>
>> PRI/SCN 메크로들 보기 피곤하기도 하구요.
>
> 전 오히려 나중에 이식성 없게 작성된 부분들로 인해 발생할 문제를 수정
> 해야 할 생각을 하니 더 피곤해 지는군요. 님께서 30년을 보장하셨으니 한
> 31년 쯤 후에는 수정이 필요할테니까요. :-)

이거 참, 30년, 난감합니다. 지금의 프로그래밍 모델이나 하드웨어 구성이 유지된다면 128bit addressing 환경 걱정은 시기상조라는 걸 말하고 싶었습니다. 유지 안된다면 애초에 sizeof(long) 걱정할 문제가 아니구요. 이어서 답변하겠습니다.

> 지금 사소한 비용을 들여 추후 큰 비용을 막을 수 있는 경우입니다. 그
> 나중의 비용이 실제 발생할 확률이 낮다구요? 누가 미래를 알겠습니까?
> 물론 선택은 본인이 하고 책임은 본인 (혹은 종종 억울한 후임) 이 지는
> 것입니다.

네 정확하게 동의합니다. 의견이 갈리는 부분은 현재와 미래의 비용에 관한 부분입니다. 저는 [u]intptr_t의 사용이 어느정도 회색 영역에 걸쳐있다고 생각합니다.

>> 희망사항일지 모르겠지만 sizeof(long) == sizeof(void *) 가정은 POSIX에 권고로 들어가도
>> 크게 이상하지 않으리라 생각합니다.
>
> 그럴리 없습니다. 정치적으로 보자면 POSIX 에 영향을 미치는 많은 사람
> 들이 C 표준화에도 관여하고 있고, 저보다 더 까탈스러운 사람들입니다.
> POSIX 에 그런 허무맹랑한 요구를 넣는 것을 보고 있을 사람들이
> 아닙니다.

예, sizeof(long) == sizeof(void *)는 허무맹랑 할 수 있지만, LP128 권고정도는 할 수 있겠죠.

>> * 첫 답변에서 적었듯 [u]intptr_t가 정답입니다. 동의합니다. 하지만 실제로 [u]intptr_t를
>> 쓰는게 얼마나 이식성에 도움이 될지엔 의구심이 있습니다.
>
> 표준이 약속한 [u]intptr_t 에 대한 의구심은 표준 전체의 영향력에 대한
> 의구심으로 보일 수 있습니다. [u]intptr_t 에 대한 의구심은 가지고
> 계시면서 C 언어의 다른 구조가 갖는 의미나 이식성에 대한 의구심은 왜
> 가지고 계시지 않은지 궁금하군요. C 표준이 다른 부분은 다른 표준에
> 정의를 맡기고 [u]intptr_t 만 정의해주고 있는 것도 아닌데 말입니다.
>
> 표준의 강제력은 시장의 동의에 의해 형성되며, 그렇게 형성된 표준의
> 힘은 생각보다 강력합니다.

표준을 무시하는 건 아닙니다. 다만 모든 규칙을 다 따를 필욘 없다고 생각합니다.

>> 아 쓰다 보니 생각이 났는데요. PRI/SCN 메크로 외에도 (c99 아닌데서 이것도 추가해줘야죠),
>> 주소나 주소마스크 상수뒤에 타입 꼬리를 뭘로 붙일지도 명확하지 않습니다. :-(
>
> 억지스럽게 만들어 낸 문제입니다. 어차피 [u]intptr_t 의 실제 type 에
> 대한 가정 없이는 주소 마스크 상수를 구성할 수 없거나, 혹은 이식성
> 있는 방법으로 주소 마스크 상수를 구성하고자 하는 경우 ([u]intptr_t)
> 캐스트로 형 변환 후 연산을 적용할 수 있습니다.

이 부분이 제가 [u]intptr_t 가 회색 영역에 있다고 생각하는 이유중 하나입니다. 어차피 어느정도 아키택쳐 의존적인 코드일 수 밖에 없고, [u]intptr_t를 사용하냐 하지 않느냐는 전체 문제에 비하면 작고 고치기 쉬운 부분입니다. 다시 이어서...

> 표준이 typedef 를 통해 제공하는 모든 type 에 대해 상수 postfix 를
> 위한 매크로를 제공하고 있는 것은 아님에 유의하시기 바랍니다. 필요성에
> 대한 충분한 검토가 있은 후에 추가된 매크로들입니다.
>
>> [...] 제가 주장하는 바는 (일반적으로 쓰이는) 32/64bit 유닉스 시스템에서
>> sizeof(long) == sizeof(void *)이 성립한다 와 [u]intptr_t이 과연 좋은 해답인지 의구심이
>> 있다 입니다. 예를 들면 "uintptr_t이 맞긴한데, 좀 지저분해. 아 모르겠다. 그냥 웬만하면
>> ulong 써도 큰 문제는 없어" 정도 입니다.
>
> "지저분해"는 좀... --;

근데 사실 좀 지저분하긴 합니다. 아키택쳐 의존적인 배경 지식과 아키택쳐 독립적인 지식이 공존하는 코드를 쓸 수 밖에 없고, 이게 별로 좋지가 않습니다. 코드 윗 부분과 상수 선언에선 bit 가지고 막 장난치다가 같은 함수 뒤쪽에 가서 "debug: dump=0x%"__PRIPTR_PREFIX"x\n" 치고 앉아있으면 이게 뭐하는 짓인가 싶기도 하고, 제너릭 하려는 코드와 그렇지 않은 코드가 섞여서 복잡만하고 포터블하지도 않습니다. 차라리 ulong을 쓰면 크기 달라졌을 때 컴파일러가 불평이라도 하죠.

[u]intNN_t 와는 다른게 [u]intNN_t의 경우는 "난 너 몰라" 와 "난 너 알아" 상태를 왔다 갔다 할 필요가 없습니다. 그 타입들에 대해선 불만 없습니다.

> 많은 프로그램이 sizeof(long) >= sizeof(void *) 에 의존하고 있으며,
> 이 가정이 깨질 경우 그와 같은 프로그램이 오동작하게 될 것이라는
> 사실은 분명합니다 - 따라서 그런 프로그램을 많이 보유하고 있다면 하위
> 호환성을 걱정하는 implementation 이 쉽게 깨지 못할 가정입니다.
>
> 하지만 많은 프로그램이 그렇게 하고 있다고 해서 그것이 올바르고 항상
> 보장받는 것은 아닙니다. 그렇게 많은 프로그램이 기대고 있는 가정임에도
> 정수-포인터 간의 변환은 이식성이 없는 구조임이 재차 확인되어 왔습니다.
> 즉, 님께서 프로그램을 작성하는 환경을 구현하는 사람들조차 정수-포인터
> 간 변환의 이식성을 인정하지 않으며, 만약 가능한 경우에 한해서라도
> 이식성 확보를 할 수 있도록 [u]intptr_t 를 제공하기로 "약속"을 한 것
> 입니다.
>
> 그 약속을 모르는 상태에서 [unsigned] long 을 사용하신다면 어쩔 수
> 없겠지만, 알고 계시면서도 [u]intptr_t 를 애써 무시하시는 이유를
> 저로서는 이해하기 어렵다는 뜻입니다.
>
> [u]intptr_t 의 사용이 unsigned long 에 비해서 그토록 어려울까요? 기존
> 코드를 모두 고치는 문제라면 어려울 수 있겠지만, 새로 시작하는
> 프로젝트에 적용하기도 어려울까요? 제게는 직접 해보지 않으신 상태에서
> 익숙한 것에 머물러 있으려 하시는 것으로 밖에 보이지 않습니다.

직접 많이 합니다만, 별로 좋은 해결책이 아니라고 생각합니다. 컴파일러가 인식할 수 있는 ptr을 저장할 수 있는 integral 형이 필요하다고 생각합니다 (권고 사항으로라도). 64bit이 보급되면서 많은 경우에 uint/ulong 혼용에서 ulong으로 통일되었고, 표준화 입장에서 바라보면 썩 좋은 상황은 아닙니다만 실사용에선 그렇게 나쁘다고 생각하지 않습니다.

>> 표준화는 일방적인 과정이 아닙니다. C99에서 새로이 채택된 많은 추가사항들이 다른 컴파일러
>> 에서 비표준 확장으로 지원되던 기능을 표준으로 인정한 것이기도 하구요. [u]intptr_t 가 표준에
>> 맞는 방법이긴 합니다만, [u]long의 사용에 비해 확실히 낫다고 하기엔 쓰다보면 자잘하게
>> 애로사항들이 있습니다.
>>
> 말씀해 보시기 바랍니다. 정리해서 csc 논의를 거친 후 살아남는 것들은
> 표준에 반영될 수 있도록 하겠습니다.

PRI/SCN 불편함, 상수 postfix, wrap은 어디서, 정확한 크기? C 표준에서 정할 수 있는 영역이 아닌 부분이 오히려 넓지 않을까요?

>> 그래서 적어도 유닉스에선 sizeof(long) == sizeof(void *) 가정은 심심치않게 쓰이는 편이고,
>> [u]long의 크기를 그렇게 정하는게 우월한 방법이라는게 중론입니다. 표준에 들어가지 않을진 몰라도,
>> 가정이 틀려질 가능성은 매우 낮습니다.
>
> 님이 작성하신 프로그램의 이식성과 가독성을 제한한다는 가정 아래에서
> 참입니다.

예, 하지만 이식성과 가독성은 다른 평가 기준입니다. 이식성을 극단적으로 높이면 관리 불가능한 코드가 나오는 경우가 종종 있습니다. 아키택쳐에대한 지식을 사용해야하는 코드가 있으면, 제너릭한 코드를 쓰는 것보다 같은 기능을 하는 코드를 각 아키택쳐별로 쓰는 게 더 나은 경우도 많구요.

> 참고로, 보다 치명적인 문제를 일으킬 수 있는 sizeof(char) ==
> sizeof(int) 인 환경도 있는데 하물며 sizeof(long) < sizeof(void *) 인
> 환경이 존재하지 않는 것은 아닙니다. 혹시나 오해가 있을까 하는 우려
> 에서 남깁니다.

오해가 있을까 하는 우려에서 저도 말씀드리면, 그런 환경이 존재합니다. 동의합니다. 그리고 C 언어 표준은 그런 환경 전체를 커버해야합니다. 일반적인 UNIX/POSIX 프로그래밍에서 어느 선까지 고려해야하는지에 대해 의견이 다를 뿐입니다. 그리고, sizeof(char) == sizeof(int)가 sizeof(long) < sizeof(void *)보다 치명적인 문제를 일으킬 가능성이 높을진 모르겠습니다. 사용 영역에 따라 다르지 않을까요?

쓰레드가 꽤 길어졌는데, 이 정도에서 마무리해도 좋지 않을까 합니다. 서로 선도 명확히 그은 것 같구요. 그냥 "무식한 프로그래머", "언어쟁이" 선에서 타협합시다. :-)

전웅의 이미지

* 이야기가 너무 길어져서 중간 중간 생략하며 답변 드립니다. 피곤한 상태
이다 보니 무슨 이야기가 어떻게 진행되고 있는지 파악하는 것도
어렵습니다. 양해 바랍니다.

> > MC68000 에도 Unix 가 올라간 적이 있습니다. 왜 위험성을 떠 안으시면서
> > "다" 라는 표현을 고집하시는지요? "우리가 접할 수 있는 대부분의 32-bit
> > 환경에선" 이라고만 표현해도 전달하시려는 바를 모두 전달할 수
> > 있습니다.
>
> 극단적으로 예외적인 상황을 언급하는 걸 그다지 좋아하지 않습니다. 실제로 경험할 가능성이 거의 없는
> 상황을 미리 무섭게 언급하는게 긍정적인 효과가 있다고 생각하지도 않구요.
>

무섭게 언급한다고 생각하진 않지만, 설사 무섭게 언급하는 것이라 해도
그와 같은 언급을 통해 이후 생산되는 코드가 보다 이식성을 갖추게 되어
그 어느 환경에서도 long 과 void * 의 크기에 대한 가정이 불필요해진다면
충분히 긍정적인 효과를 거둔 것이라 생각합니다.

과거 intN_t, INTN, intN 등 다양한 형태로 사용되던 특정 개수 비트를
의미하던 정수 type 들이 C99 이후 점차 표준화된 형태를 사용하는 방식
으로 수렴해 가고 있습니다. 님이 원하시든 그렇지 않든 단언컨데
정수-포인터 변환을 위한 type 역시 [u]intptr_t 를 관례적으로 사용하는
시대가 오리라 예상합니다.

> 원 문장은 "다 ~ 라고 생각하시면 되구요" 입니다. "다 ~ 입니다" 가 아니죠. MC68000에 올라간 유닉스는
> 저 정도 표현으로 커버된다고 하면 안될까요?
>

애초부터 "다"가 중요했던 것이지 "-라고 생각하시면 되구요"가 중요했던
것이 아닙니다. 하지만, 전 제 의사를 전달했고, 님께서도 처음 말씀하신
부분에 어떠한 형식으로든 수정이 필요함을 인식하고 계시니 더이상 크게
중요한 문제는 아니라고 봅니다.

> C 언어 표준이 UNIX를 아우르긴 하지만 UNIX가 C 표준만을 따르진 않습니다. 표준이 있고, (주로 그
> 안에서 혹은 경계선에 걸쳐서) 따로 관행도 있구요.
>

흠... 표준에 대해 오해하고 계십니다. C 표준이 정의해 주지 않는 부분,
요구하지 않는 부분에서 POSIX 를 비롯한 다른 표준이 의미를 추가로
부여해 주고 있는 것입니다. C 표준화 과정에 C++, POSIX, LIA, IEEE 754
등을 담당하는 표준화 위원회와 꾸준히 협의하는 과정이 포함됩니다. UNIX
를 놓고 여러 표준이 땅따먹기 하고 있는 것이 아닙니다. 실제로 정의해
줄 필요가 있지만 C 표준에선 부적절하다고 판단되는 부분들은 POSIX 에
요청해 넘기기도 하는 일이 빈번했습니다.

[...]

> 코드 윗 부분과 상수 선언에선 bit 가지고 막 장난치다가 같은 함수 뒤쪽에 가서 "debug: dump=0x%"__PRIPTR_PREFIX"x\n"
> 치고 앉아있으면 이게 뭐하는 짓인가 싶기도 하고, 제너릭 하려는 코드와 그렇지 않은 코드가 섞여서 복잡만하고
> 포터블하지도 않습니다. 차라리 ulong을 쓰면 크기 달라졌을 때 컴파일러가 불평이라도 하죠.
>

어차피 정수로 변환된 포인터 값의 결과가 이식성이 없다고 할 때, 포인터
값 출력에는 %p 를 사용할 수도 있습니다. "대부분"의 구현이 정수형으로
변환해 출력해주게 됩니다.

제너릭 하려는 코드와 그렇지 않은 코드가 섞이는 것은 구현체 의존적인
부분에 대한 분리가 부족하거나 이식성을 확보할 수 있는 방법에 대한
고민이 부족한 경우에 주로 발생합니다. 물론, 과거부터 존재하던 코드에
대한 유지 보수 과정에서 이와 같은 새로운 개념 도입이 어렵다는 사실은
인정합니다. 하지만 처음부터 설계가 시작되는 부분이라면 충분히 바로
잡아 나갈 수 있는 부분입니다.

ulong 을 썼을 때의 더 무서운 점은 해당 프로그램이 ulong 이 포인터 값을
온전히 표현하지 못하는 환경으로 갔을 때 "조용히" 오작동 할 수 있다는
것입니다. 반면, uintptr_t 를 사용하며 조건부 컴파일을 적용한
프로그램은 (#error 를 통해) 무엇이 문제인지를 분명히 지적해 주게
됩니다.

> 직접 많이 합니다만, 별로 좋은 해결책이 아니라고 생각합니다. 컴파일러가 인식할 수 있는 ptr을 저장할
> 수 있는 integral 형이 필요하다고 생각합니다 (권고 사항으로라도). 64bit이 보급되면서 많은 경우에
> uint/ulong 혼용에서 ulong으로 통일되었고, 표준화 입장에서 바라보면 썩 좋은 상황은 아닙니다만
> 실사용에선 그렇게 나쁘다고 생각하지 않습니다.
>

"C 표준"의 권고를 의미하시는 것이라면 불가능한 이야기입니다.

CHAR_BIT == 9
sizeof(char) == sizeof(int)
CHAR_BIT * sizeof(int) == 36
실제 머신 바이트의 비트수 == 7 && CHAR_BIT == 32 && sizeof(int) == 1

64-bit 시대가 다가 왔다고 해서 다양한 임베디드 시스템에 사용되는 이런
괴상한 구현체 들을 무시할 수 있는 것은 아닙니다. C 언어는
32-bit/64-bit UNIX 나 Windows, 혹은 그 위에 돌아가는 응용 프로그램만을
위한 언어가 아닙니다.

long 형이 void * 를 정보 손실 없이 담을 수 없는 환경이 단 하나라도
존재하는 이상 언어 표준은 이를 강제할 수 없습니다.

* [u]intptr_t 사용과 관련된 질문들:

> PRI/SCN 불편함,

정수-포인터 변환을 수행하는 프로그램들은 사실 그렇게 변환된 정수값을
입출력할 일이 거의 없습니다. 더구나 이미 printf/scanf 에 %p 를
제공하고 있는 상황입니다. PRI/SCN 매크로의 완전성을 위해 추가된 부분
이긴 하지만, 꼭 사용해야 한다면 이식성 있는 프로그램을 작성할 때
들어가는 비용으로 볼 수 있습니다. long 형을 사용할 경우 들어가는 비용
은 지금은 눈에 보이지 않지만 미래에 잠재되어 있는 것일 뿐이지요.

> 상수 postfix,

필요 없습니다. 상수 postfix 를 필요로 한다는 것 자체가 [u]intptr_t 에
대해 오해하고 있거나 불필요한 가정을 하고 있다는 반증입니다.

> wrap은 어디서,

표준에서 유부호 정수형은 wrap 이 정의되지 않습니다 - overflow 일
뿐이고 그 이후 행동은 정의되지 않습니다. 무부호 정수형은 0 미만 최대값
(UINTPTR_MAX) 초과에서 wrap 합니다. [u]intptr_t 는 어떤 특수한 정수형
이 아닙니다. 표준이 다른 정수형에 요구하는 특성은 그대로 품고
있습니다.

> 정확한 크기?

<stdint.h> 에 매크로로 정의되어 제공됩니다. 비트 단위 크기는 CHAR_BIT
* sizeof(uintptr_t) 로 구할 수 있으나, 다른 정수 type 과 마찬가지로
padding bit 가 존재한다면 bit 단위 크기와 이를 이용해 추측한 표현 가능
한 값의 범위에 차이가 날 수 있습니다.

질문으로 추측컨데 님께서 [u]intptr_t 에 대해 오해하고 계신 부분이 적지
않은 것 같습니다. 아마도 C99 자체가 발표한지 10년이 다 되어 감에도
아직 널리 알려지지 못한 탓이 크지 않나 생각합니다. 이름도 익숙치 않고
type 자체가 typedef 이름 뒤에 숨어있다는 사실이 실제 코딩 과정에서
골치 아프게 느껴질 수도 있겠습니다만, 한번 익숙해지고 나면 [u] long 을
사용하는 것과 크게 다르지 않다는 것을 아실 수 있습니다.

[...]

> 예, 하지만 이식성과 가독성은 다른 평가 기준입니다.

unsigned long 형으로의 변환보다 uintptr_t 로의 변환이 그 의도를 보다
분명히 표현한다는 점에서 가독성을 언급한 것입니다.

> 이식성을 극단적으로 높이면 관리 불가능한 코드가
> 나오는 경우가 종종 있습니다. 아키택쳐에대한 지식을 사용해야하는 코드가 있으면, 제너릭한 코드를
> 쓰는 것보다 같은 기능을 하는 코드를 각 아키택쳐별로 쓰는 게 더 나은 경우도 많구요.

예 맞습니다. 하지만 문제는 포인터를 담기 위해 long 형을 사용하는 것은
아키텍쳐 별로 분리해 쓰기가 어렵다는 점입니다. 오히려 상황에 맞춰
분리해 사용하는 방법은 제가 이전에 조건부 컴파일을 사용한 방법으로
보인바 있습니다.

> sizeof(char) == sizeof(int)가 sizeof(long) < sizeof(void *)보다 치명적인 문제를 일으킬 가능성이
> 높을진 모르겠습니다. 사용 영역에 따라 다르지 않을까요?

참고로 드리는 말씀이지만, sizeof(char) == sizeof(int) 일 경우
<stdio.h> 를 사용하는 거의 모든 프로그램이 오작동할 수 있습니다.

* 이런 저런 이야기가 나온 탓에 제가 전달하려는 바가 묻힌 것 아닌가
걱정스럽습니다. "32-bit 는 다" 라는 표현 때문에 프로그램에서 정수형을
사용하는 방법, 표준에 대한 일반적 이야기, LP32, LP64, LLP64 등등
잡다한 이야기가 언급되었지만, 제가 전해 드리고 싶은 핵심은

- [u] long 형과 포인터 간의 자유로운 변환이 과거 코드에서 가정된 바
있으나 C 표준, POSIX 표준 그 어디에서도 이를 보장하는 부분은 없다

- 하지만 그와 같은 변환을 제공하는 환경에서 이를 이용하는 프로그램의
이식성 향상을 위해 C99 는 optional type 인 [u]intptr_t 를 제공한다

- long 과 포인터 사이의 변환을 수행하는 기존 코드를 새 방식에 맞춰
수정한다는 것은 물론 쉬운 일이 아니다. 하지만, 새로 시작되는 코드에
있어서만큼은 [u]intptr_t 를 통해 보다 확실한 이식성을 확보할 수 있다

- [u]intptr_t 의 의미와 존재에 대한 약속은 C 언어가 존재하고 표준이
존재하는한 보장된다

- long 형과 포인터 간의 변환이 다수 환경에서 보장되는 근거는 backward
compatibility 때문이다. 이는 (POSIX 역시 C 표준을 normative
reference 로 참조한다는 점을 고려할 때) [u]intptr_t 가 도입된 이후
널리 사용되면 느리더라도 점차적으로 폐기될 수 있는 가정이다

- 만에 하나 [u]intptr_t 를 사용하면서 프로그래머의 추가적인 수고가
필요하다면 이는 미래 발생할 수 있는 규모 있는 문제를 배제하기 위한
현재의 소규모 비용으로 생각할 수 있다

- 실제 long 형이나 unsigned long 형을 통해 포인터-정수 변환을 수행하는
코드를 작성하는 것과 [u]intptr_t 를 사용해 동일한 코드를 작성하는
것에는 큰 차이가 없다.

입니다.

정말 long-포인터 간의 변환 문제는 약간의 수고와 [u]intptr_t 에 대한
이해만으로 손 쉽게 이식성 있는 형태로 변환될 수 있는 문제입니다.

주저리 주저리 떠들었더니 배고프군요. --;

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

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

tj의 이미지

예, 자세한 답변 감사합니다. 서로 한 말을 반복하는 상황이 된 거 같은데, 누차 말씀 드렸듯, [u]intptr_t가 C 표준이고, 정답 맞습니다. 저도 갸우뚱거리면서도 웬만하면 씁니다. 그 다음 제 주장 부분도 자세한 답변에도 불구하고 그냥 반복이 될 듯 합니다. 맛있는 거 드시고 안녕히 주무세요.

전웅의 이미지

> 예, 자세한 답변 감사합니다. 서로 한 말을 반복하는 상황이 된 거 같은데, 누차 말씀 드렸듯,
> [u]intptr_t가 C 표준이고, 정답 맞습니다. 저도 갸우뚱거리면서도 웬만하면 씁니다. 그 다음 제 주장
> 부분도 자세한 답변에도 불구하고 그냥 반복이 될 듯 합니다. 맛있는 거 드시고 안녕히 주무세요.

지난 제 글은 주제가 되는 문제에 대해 님의 글에 직접적인 반박을 하기
위한 의도라기 보다는 (님의 주장과는 무관하게) 논의가 이어지면서 주제가
불분명해진 까닭에 글 읽는 다른 분들을 위해 제가 드리고 싶은 말씀을
정리한 것 뿐입니다.

쓰래드를 현명하게 잘 이끌어 주셔서 감사합니다. 님도 편안한 밤 보내시기
바랍니다.

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

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