컴퓨터공부/C & C++ & STL

[펌] #5 null pointer

achivenKakao 2005. 6. 30. 15:04

Q : 5.1
null pointer가 무엇인가?

A :
다른 포인터와 구분할만하게 다르고, 어떤 object나 function과 비교해도 다른 것이 보장된다.
&연산자를 이용해서 주소를 얻을 때는 당연히 절대 null pointer를 만들어 낼 수 없고
malloc을 이용해서 성공적으로 부를 수 있다.(보통 malloc은 실패시 null pointer를 return한다.
이것이 일반적인 null pointer의 사용이다.)
--> not allocated or not pointing anywhere yet 인 특별한 pointer이다.

개념적으로 null pointer와 uninitialized pointer는 다르다. null pointer는 어떤 object나
function도 가르키기 않는반면 uninitialized pointer는 분명 어딘가 가르키고 있다.
(원하는 곳을 가르키지는 않을테지만)

각각의 pointer type마다 null pointer가 있고 null pointer의 내부 값들은 각 타입에 따라서
다를 수가 있다. 프로그래머가 이 값을 알 필요는 없지만 컴파일러는 어떤타입의 null pointer
인가를 알기 위해서 알아야 한다.

Q : 5.2
어떻게 null pointer를 얻을 수 있는가?

A :
언어의 정의에 따라 상수 0은 컴파일 시간에서 null pointer로 변환된다. 초기화(initialization),
선언(assignment), 비교(comparision)을 한쪽이 포인터 타입일 때 하면, 컴파일러는 상수 0이
null pointer라 말할 수 있고, 올바른 형식의 null pointer value를 생성한다. 그러므로 다음식은
아주 합법적이다.

char *p = 0;
if(p != 0)

그러나 함수에 인자로 전달되는 값일 경우에는 포인터 라고 말할 수 없다. 이런 이유때문에
상수 0이 null pointer를 의미한다고 컴파일러는 말할 수 없다. 함수호출시 null pointer를
생성하기 위해서 명시적 형변환이 필요하다.
명시적 형변환을 까먹는다면 null pointer를 의미하는 값을 전달하는 대신에 integer 0을
전달할 것이다.
함수의 prototypes이 시야내에 있을 때(in scope), 형변환을 빼먹어도 prototype이 컴파일러에게
알려주기 때문에 형변환이 잘된다. 그러나 함수의 prototype이 variable-length argument lists에
있는 다양한 인자(variable arguments)의 types을 제공하지는 못한다. 즉 integer 0으로 받아들일
수도 있다는 뜻이다. 따라서 null pointer를 함수의 인자로 넘길 때는 명시적 형변환을 해주는
것이 좋다.

Q : 5.3
if(p) 가 non-null pointers를 검사하기에 알맞은 식인가? 내부 표현(internal representation)이
nonzero일수도 있을텐데?

A :
if, while, for, do 식와, &&, ||, !, ?: operators에서 boolean 값을 원할 때, 비교결과가
0이면 false이고 아니면 true이다.
if(expr)
와 같은 식은 사실은
if((expr) != 0)
컴파일러와 위와 같이 행동한다. 즉
if(p) is equivalent to if(p != 0)
그리고 이 비교식에서, 컴파일러는 암시적으로 null pointer라고 말할 수 있다. 그래서 올바른
null pointer 를 사용할 수 있는 것이다. 여기에 문제될만한 부분이 전혀 없다.
if(p)와 같은 함축식(Abbreviations)은 지극히 '합법'적이지만 일부 사람들은 나쁜 스타일이라고
생각한다.(그리고 그 나머지 사람들은 좋은 스타일이라고 생각한다.)

Q : 5.4
NULL은 무엇이고 어떻게 정의하는가?
A :
스타일상 많은 프로그래머들은 꾸며지지않은(unadorned) 0을 쓰는 것을 좋아하지 않는다. 그래서
전처리기에서 0을 NULL로 지정한다. 따라서 integer 0과 null pointer 0을 구분해서 사용한다.
단지 스타일상 바꾼것이다. 5.2에서 언급한 함수에 인자로 넘겨줄 경우의 형변환은 NULL을
사용한다고 해도 당연히 해주어야 한다.

Q : 5.5
null pointer의 내부 표현(internal representation)으로 nonzero bit pattern을 사용하는
machine에 NULL은 어떻게 정의 되는가?

A :
0으로 정의된다. 내부 표현으로 바꾸는 것은 컴파일러의 몫이다.

Q : 5.6
#define NULL ((char *)0)
이라고 정의하면 함수 call을 할 때 NULL을 uncast해도 되는가?

A :
일반적으로는 안된다. 문제는 각 포인터 타입에 대해서 내부 표현이 기계에서 다를 경우이다.
char* 를 함수에 넘겨줄 때는 문제가 없다. 하지만 다음과 같은 경우 문제가 생길 수 있다.
FILE *fp = NULL;

ANSI C에서는 다음과 같은 대안이 있다.
#define NULL ((void *)0)

잠재적으로 잘못된 프로그램이 작동하는데 도움을 주기도 하고, NULL을 잘못 사용했을 경우를
발견하는데도 도움을 준다.

Q : 5.9
NULL, 0 이 같다면 몰 사용해야 되나요?
A :
어떤이는 NULL이 반드시 모든 포인터에 사용되어야 한다고 믿고 (값이 pointer라는 것을 상기함)
어떤이는 0을 macro뒤에 숨기는 정도라고 느끼면서 0을 사용하는 것을 더 좋아한다.
옳은 답은 없다.
그러나 NULL은 다른 종류의 0에서 사용하면 안된다.(비록 작동은 하겠지만) 이렇게 하는 것은
잘못된 스타일이다. 특히 ASCII null character(NUL)이 필요한 곳이 절대 사용하지 말라.
#define NUL '\0'
위의 정의를 이용해서 NUL을 사용해라.

Q : 5.10
NULL값이 바뀌는 경우에는 NULL을 사용하는게 도 좋지 않나? 아마 machine은 내부에서 null
pointer로 nonzero를 사용하지 않나?
A :
아니다! NULL을 사용하는것이 좋을수도 있다. 하지만 위의 이유는 아니다.

Q : 5.12
#define Nullptr(type) (type*)0
위와 같이 쓰는 것은 올바른가?
A :
이 기술은 유명하고 표면적으로는 매력적이지만 별로 안좋다. 이것은 assignment와 비교에서
필요 없다. keystrokes에도 별 도움이 없다. null pointer사용자에게 혼란만 주고 괜히
#define 만 필요로 하고 암튼 별로다.

Q : 5.13
것참 이상하군요. NULL은 0인게 보장이 되는데 null pointer는 그렇지 않다니 이상합니다.
A :
null이라는 용어와 NULL이라는 용어가 일상적으로 쓰일 때, 다음과 같은 의미를 지닌다.

1. 개념상 null pointer는 5.1 질문에 정의되어 있다.
2. null pointer의 내부 표현은 모두 0일수도 있고 아닐수도 있고 type마다 다를수도 있다.
올바른 값을 컴파일러 작성자가 고려하고 만든다. C 사용자는 볼수도 없다.
3. null pointer constant 라는 것은 상수 integer 0을 의미한다.
4. NULL macro 는 #define 0 이거나 (void *)0을 의미한다.
5. ASCII null character(NUL)은 모든 bit가 0이지 않지만 null pointer와 상관없는 놈이다.
6. null string은 빈 문자열(empty string "")의 다른 이름이다.

Q : 5.14
왜 null pointer에 관련한 헷갈리는 사항들이 이렇게 많은가? 왜 질문들이 이렇게 많고 자주있지?
A :
C 프로그래머는 전통적으로 기계 내부 구현에 관한 사항까지 알고 싶어한다. null pointers는
소스코드와 대부분의 기계내부에서 0으로 표현된다는 점은 확실하지 않은 가정을 야기한다.
NULL 을 사용하는 것은 값이 변할 것이라고 보인다. if( p == 0 )표현식은 0이 pointer type
이라고 보이기보다 p가 integral type이라고 보이게 한다. null이라는 용어도 헷갈린다.

헷갈리지 않게 하기 위해서 null을 nil이라는 용어로 생각해보자. 컴파일러는 nil을 변환이
가능하면 올바른 null pointer값으로 변환하고 안되면 안된다고 불만을토로한다(complain)
자 이제 nil( null pointer ) 값을 사실 nil이 아니로 0이다. 대부분의 경우에는 이 것이
잘 작동하지만 uncast 0이 integer zero를 생성하는 경우에, (에러 메세지를 안만들고) 또
uncast 0이 null pointer constant라고 가정했다면 이 코드는 작동안할지도 모른다.
--> 했던 얘기 또하는구나.

Q : 5.15
null pointer라는 것을 도저히 이해 못하겠다.
A :
다음의 간단한 규칙을 따라봐라.
1. null pointer constant 를 소스 코드에서 원할 때 0이나 NULL을 이용해라.
2. 0이나 NULL을 함수 호출에 사용할 경우 형변환을 해라.
Q : 5.16
주어진 모든 null pointer에 관한 혼란을 생각하면, 내부에서 zero로 간단히 표현하는게
좋지 않을까?

A :
다른 이유가 아니라면 그렇게 하라는 것은 나쁜 조언이다. 왜냐면 그렇게 하지 않을 경우에
nonzero bit pattern이 invalid accesses를 위한 하드웨어의 덧을 자동적으로 발생하게 할 때
쓸데 없는 제약이 되기 때문이다.

게다가 이렇게 하면 무엇을 얻을 수 있는가? null pointer는 내부 표현에 대한 정보는 필요로
하지 않는다. 0이든 아니든 전혀 상관 안한다. 내부표현에서 null pointer가 0이라고 해서
코드를 쉽게 하거나 하는 등의 이득이 없다. 알려진 0을 내부 포인터로 쓰는 경우에도 cast문제
를 없애지는 못한다. 왜냐면 포인터의 size에 관련한 값은 여전히 다르기 때문이다.

Q : 5.17
nonzero null pointer를 내부에서 사용하는 기계가 진짜로 있는가? 또, 포인터 타입이 다를 때
다른 표현(representation)을 포인터에 사용하는 경우도 진자로 있는가?

A :
The Prime 50 : 07777
Honeywell-Bull : 06000
CDC Cyter 180 series : 0xB0000000000
HP 34000 series : byte address와 word address의 경우 null pointer를 다른 것을 썼다.
즉 char*와 void*의 내부 표현식이 다른다.

등등 두가지 경우 모두 진짜로 있다.

Q : 5.20
run-time에서 'null pointer assignment' error가 났는데 이건 모고 어떻게 검사(track)하는가?
A :
이 메시지는 MS-DOS 컴파일러에서 나타나는데 null pointer를 이용해서 쓰려고 했는데
따라서 location 0에다가 쓰려고 했다는 뜻이다.
디버거를 이용해서 breakpoint나 watchpoint를 location 0에 놓고 볼 수있다. 또 다른 방법으로
location 0으로부터 20 bytes정도의 복사본을 간직하는 코드를 만들고 주기적으로 location 0
근처의 메모리가 변하는지 확인할 수 있다.