C++ NULL에 대해서 질문합니다.
글쓴이: hojak99 / 작성시간: 화, 2016/11/29 - 1:50오전
void aa(int){ std::cout<<"Call aa "<<std::endl } void aa(int *) { std::cout<<"Call *aa"<<std::endl; } int main() { aa(NULL); return 0; }
위의 코드를 출력시켰을 때 "Call aa"를 출력하게 됩니다. "Call aa"를 출력하는 이유가 NULL이 0을 뜻하고 있어서 인가요?
뭔가 확실하게 아는 것 같지 않아서 질문 합니다.
Forums:
NULL은 C++ 타입시스템의 노출된 특이점 같은
NULL
은 C++ 타입시스템의 노출된 특이점 같은 존재입니다.이 녀석은 포인터가 들어갈 수 있는 자리라면 그게 무슨 타입을 가리키는 포인터든 들어갈 수 있어야 한다는 사명을 부여받았는데, 생각해 보면 그게 가능한 C++ 타입은 없어요.
네,
void *
를 생각하시는 분이 계시겠죠. 하지만 어떤 포인터든void *
가 될 수는 있어도, 그 역, 그러니까void *
가 다른 포인터로 함부로 변환될 수는 없는 거죠.덕분에 C++에서
NULL
의 타입은 붕 뜨게 되는데, 정확히 어떤 타입일지는 구현환경마다 다릅니다. 심지어NULL
이 그냥 정수 0으로 정의되어 있을 수도 있어요. 일견 황당하지만, 실제로 컴파일러가 특정 포인터 타입을 더 선호할 이유는 없다는 점에서, 그리고 많은 C/C++ 프로그래머들이NULL
대신 그냥 0을 쓰기도 한다는 점에서 그럴듯한 판단이죠. 근데 그러면 질문자님이 보시는 것과 같은 사태가 일어나는 겁니다.평소라면 "에이, 까짓것 그냥 강제로 캐스팅해버리거나 그냥 경고 띄우게 냅두고 무시하자" 해 버리면 되지만, 질문자님의 경우처럼 오버로딩 버전 선택을 위해 사용하거나 템플릿 인자 추론을 맡긴다던가 할 때가 되면 무시 못 할 문제가 생기게 됩니다. 이럴 땐 명시적으로 NULL을 캐스팅해서 넘기는 수밖에요.
static_cast<int *>(NULL)
하면 되겠죠...?물론 이 꼴을 보다 못한 C++의 높으신 분들께서 친히 어여삐 여기시어 C++11부터는
nullptr
라는 상수가 추가됐습니다. 이 놈은std::nullptr_t
라는 희한한 타입을 가지는데, 실상 앞서 언급한 타입시스템의 특이점을 살짝 가려 주는 역할을 합니다. 이 상수를 쓰면 별도로 캐스팅하지 않아도 항상 포인터를 받는 버전이 선택될 겁니다. (물론 포인터를 받는 오버로딩 버전이 둘 이상이면 여전히 문제죠. 근데 그건 C++ 탓이 아닙니다. 그렇게 짠 사람 탓이지.)감사합니다.
감사합니다! 무슨 말씀하시는지 알겠습니다
너무 여럽게 설명한 것 같습니다.
너무 여럽게 설명한 것 같습니다.
NULL은 단지
으로 정의된 메크로에 불과합니다. 이게 정의이고, 정의가 이렇기 때문에
"타입이 없다." 라는 특징이 나오는 것이구요.
C++과 C는 다릅니다. 너무 쉽게 생각하시는 듯.
C++과 C는 다릅니다.
너무 쉽게 생각하시는 듯.
C++의 표준헤더에 있는 NULL도 macro로
C++의 표준헤더에 있는 NULL도 macro로 정의됩니다.
http://www.cplusplus.com/reference/cstddef/
gcc의 cstddef.h 문서: https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-3.4/cstddef-source.html
clang의 stddef.h 문서: http://clang.llvm.org/doxygen/stddef_8h_source.html
문서 참조하십시오.
태클을 걸어주신 덕분에 더 찾아보게 되는군요. 그런
태클을 걸어주신 덕분에 더 찾아보게 되는군요. 그런 재미로 답변 다는 거죠.
제시하신 코드는
NULL
의 정의가 아닙니다.NULL의 정의는 C11, C++03, C++11 셋 모두에서 아래와 같이 제공합니다.
약간의 표현 차이는 있지만 중요하지 않아서 그냥 정리했습니다. 요점은 implementation-defined와 null pointer constant거든요.
여기서 null pointer constant의 의미는 표준에 따라 아주 미묘하게 다릅니다.
C언어에서는
((void *)0)
이NULL
의 정의일 수 있지만 C++03과 C++11에서는 그렇지 않군요. C언어에서는 정수 0뿐만 아니라 0을(void *)
로 캐스팅한 값도 null pointer constant인 반면, C++03에서는 0으로 평가되는, 정수 타입의 정수 상수 표현식이 null pointer constant라고 규정하고 있기 때문입니다. C++11에서는 여기에std::nullptr_t
관련 내용이 추가됐을 뿐이고요.이렇게 되면 사실 제 원래의 답변에도 흠집이 생기게 됩니다. C++03 기준으로는
NULL
이 정수 0으로 정의되어 있을 수 있다가 아니라, 그냥 확실히 정수 상수 0이다라고 해야겠군요. 구체적인 타입은int
이든unsigned int
이든 달라질 수도 있겠지만요.역시 답변을 달려면 제대로 알아보고 해야 하는 모양입니다. 쩝.
이제 왜 질문자님의 코드에서
NULL
이 정수를 받는 오버로딩 버전을 선택하는지도 분명해집니다.NULL
이 애초에 그냥 정수 타입의 정수였으니까요. 물론NULL
이int
인지 아닌지는 알 수 없습니다만, 오버로드된 함수를 호출할 때 어떤 함수가 선택되는지에 대한 미친듯이 복잡한 표준 규칙을 찾아보면 아무래도 상관 없이int
를 받는 버전이 선택된다는 점을 알 수 있습니다. Standard conversions, 그 중에서도 Integral promotions 또는 Integral conversions이 적용되거든요.오컴의 면도날 같은 있어보이는 이름은 차치하고서라도, 쉬운 설명과 어려운 설명이 있다면 저도 물론 쉬운 설명을 선호합니다. 하지만 그 이전에 1)정확해야 하고 2)질문자님의 질문을 커버할 수 있어야 하지요.
저는 최선을 다했습니다. twinwings님의 답변은 어떠한가요? 개인적으로 저는
((void*)0)
라고 직접 명시적으로 캐스팅하셨으면서도 타입이 없다고 말씀하신 부분에 대한 이론적 근거가 무척 궁금합니다. 또한 그 내용이 C++에서의 오버로딩 버전 선택을 묻는 질문을 어떻게 커버하고 있는지에 대해서도 의문이 있고요.감사합니다
전에 답글 달아주신 것보고 무슨 말씀을 하시는 것인지 알 것 같아서 구글에 좀 더 검색을 해보고 예제도 보았는데 이번 답글보고 감사할따름입니다 ㅠㅠㅠ
제가 말한 정의는 음.. 단어의 정의가 아니라
제가 말한 정의는 음.. 단어의 정의가 아니라
prototype과 implementation의 관점에서의 정의를 뜻합니다.
컴파일(링크) 할 때 "정의를 찾을 수 없습니다."와 같은 표현으로요.
오해할 수 있겠군요.
표준을 따르는 implementation에서의
아니요. 저는 오해하지 않았습니다.
다만 표준을 따르는 implementation에서의
NULL
정의는 당연히 표준의 정의를 따라갑니다.당연하지요. 그러라고 있는 표준이니까요.
꼭 표준에서의 정의가 아닌 실제 implementation에서의 정의를 보고 싶으시다면, 멀리 갈 것도 없습니다.
직접 링크하신 clang의 stddef.h 문서도 제가 말씀드린 내용의 좋은 예가 되는군요.
C언어에서는
NULL
이 정수 상수 0일 수도 있고(void *)
로 캐스팅된 정수 상수 0일 수도 있지만(후자를 택했군요) C++의 경우는 반드시 정수 상수 0이어야 한다는 표준 규정을 훌륭하게 준수하는 모습입니다.링크하시진 않았지만, gcc 4.8.4의 stddef.h도 대략 비슷합니다.
이 질문은 명백히(제목은 차치하고서라도, C언어에 함수 오버로딩이 있던가요?) C++ 질문이므로 C++의 규칙을 따라야죠.
그건 그렇고, 표준에도 분명히 명시되어 있듯
NULL
은 확실히 매크로가 맞습니다만, 그 사실이 이 질문에 있어서 무슨 의미를 가지지요?C는 void* 형과 다른 포인터 형이 호환되기
C는 void* 형과 다른 포인터 형이 호환되기 때문에 ((void*)0)과 같이 정의될 수 있습니다.
하지만 C++에선 그렇지 않기 때문에 보통 0을 씁니다. implementation에서 정의된 다른 내부적인 구문을 쓸 수도 있겠지만, 적어도 ((void *)0)은 쓸 수 없습니다.
C++11에서는 거기에 nullptr_t같은게 추가된 모양이군요
댓글 달기