윈도우에서 File의 현재 포인터를 변경 하고자 할 때 사용하는 SetFilePointer()라는 함수가 있다.
재미있는 것은 새로 옮길 파일의 위치를 가리키는 변수로 LONG lDistanceToMove, PLONG lpDistanceToMoveHigh 두개를 사용하고 있다는 점이다. 
이유는 4Byte의 주소로는 4GByte이상의 큰 파일에서 한계가 있기 때문이다.

왜 POSIX [ off_t lseek(int fildes, off_t offset, int whence) ]와 같이 새로운 타입을 정의하여 long long형을 사용하지 않았을까? 하는 의문이든다.

어째뜬, SetFilePointer() 함수는 4Byte 형 두개의 변수를 8Byte 변수로 합쳐야 할 필요가 생긴다.
하지만, 이 역시 간단하지 않다.

오늘은 이 이야기를 하고자 한다.

#define INT_TO_LONGLONG(high, low)            ((((long long) (high)) << 32) | (low))

int 형 변수 두개를 long long형으로 만드는 매크로 함수 이다. 적당히 shift 하고 OR연산하고 있다.
얼핏 보면 문제가 없어 보이지만, 아래 예제를 실행해 보면 이상한 점을 발견할 수 있다.
#define ULONGLONG_TO_LOW_UINT(eight)		((unsigned int) ((eight) & 0x00000000FFFFFFFFULL))		
#define ULONGLONG_TO_HIGH_UINT(eight)		((unsigned int) (((eight) >> 32) & 0x00000000FFFFFFFFULL))
#define INT_TO_LONGLONG(high, low)			((((long long) (high)) << 32) | (unsigned int)(low))

int main()
{
	long long	input = 3221225472;
	int			low;
	int			high;
	long long	output;

	low = ULONGLONG_TO_LOW_UINT(input);
	high = ULONGLONG_TO_HIGH_UINT(input);

	output = INT_TO_LONGLONG(high, low);	

	printf("input = %lld, \t", input);
	printf("output = %lld", output);

	return 0;
}
이 프로그램의 결과는 다음과 같다.
input = 3221225472,     output = -1073741824

이유를 살펴보도록 하자.

3221225472는 16진수로 표현하면 0xC0000000 이다. 4byte로 표현이 되므로 high는 0, low는 0xC0000000이 들어가게 된다.
문제는 변수의 최상위 bit이 1이면 그 수는 음수로 인식이 된다는 것이다.

INT_TO_LONGLONG 매크로는 high 를 왼쪽으로 32bit만큼 shift한다면 low를 OR연산하고 있다.
high는 0이므로 shift해도 0이다. 하지만, low를 OR 할 때 컴파일러는 0xC0000000를 음수로 인식하여 계산결과는 0xC0000000이 되지 않고, 0xFFFFFFFFC0000000 이 되어 -1073741824 로 저장되는 것이다.

참 똑똑한(?) 컴파일러이다.
개발자는 어떤 임플리멘테이션으로 컴파일 될지 모르기 때문에 애매한 표현은 하지 말아야 한다.

따라서 INT_TO_LONGLONG 매크로는 다음과 같이 고쳐져야 한다.

#define INT_TO_LONGLONG(high, low)            ((((long long) (high)) << 32) | (unsigned int)(low))

unsigned 로 명시하여 애매모호함을 없앴다.
하지만,  high가 0이고 low 가 -1 일때는 어떻게 될까? 과연 output도 -1이 될 수 있을까?

 그렇지 않다.
-1은 16진수로 표현하면 0xFFFFFFFF이다. 위 INT_TO_LONGLONG을 거치면 0x00000000FFFFFFFF 가 되어 양수가 된다.

이사항 까지 고려 한다면 INT_TO_LONGLONG 매크로는 다음과 같이 되어야 한다.

#define INT_TO_LONGLONG(high, low)            ((high) ? ((((long long) (high)) << 32) | (unsigned int)(low)) : (low))
신고
  1. Favicon of http://blueasa.tistory.com BlogIcon blueasa

    | 2012.01.09 12:02 신고 | PERMALINK | EDIT | REPLY |

    좋은 정보 감사합니다. :)

  2. Favicon of http://rudalson.tistory.com BlogIcon 없다캐라

    | 2014.06.26 14:05 신고 | PERMALINK | EDIT | REPLY |

    재밌게 잘 봤습니다. 이부분 캐스팅때 생각보단 고려해야 될게 많네요...

    전체 예제 코드중에서 이부분
    #define INT_TO_LONGLONG(high, low) ((((long long) (high)) << 32) | (unsigned int)(low))

    (unsigned int) 부분이 빠져야 되는것 아닌가요?
    설명을 읽다 보니 저렇게 변경되었다는 것을 알겠네요.

Write your message and submit
« PREV : 1 : ... 19 : 20 : 21 : 22 : 23 : 24 : 25 : 26 : 27 : ... 228 : NEXT »