나는 C ++을 배우고 있으며 null을 이해하는 데 어려움을 겪고 있습니다. 특히, 내가 읽은 자습서는 "널 체크"를 수행한다고 언급했지만 그것이 무엇을 의미하는지 또는 왜 필요한지 잘 모르겠습니다.
- null이 정확히 무엇입니까?
- "널 확인"은 무엇을 의미합니까?
- 항상 null을 확인해야합니까?
모든 코드 예제는 대단히 감사하겠습니다.
나는 C ++을 배우고 있으며 null을 이해하는 데 어려움을 겪고 있습니다. 특히, 내가 읽은 자습서는 "널 체크"를 수행한다고 언급했지만 그것이 무엇을 의미하는지 또는 왜 필요한지 잘 모르겠습니다.
모든 코드 예제는 대단히 감사하겠습니다.
답변:
C 및 C ++에서 포인터는 본질적으로 안전하지 않습니다. 즉, 포인터를 역 참조 할 때는 포인터가 유효한 위치를 가리키는 것이 본인의 책임입니다. 이것은 "수동 메모리 관리"의 일부입니다 (Java, PHP 또는 .NET 런타임과 같은 언어로 구현 된 자동 메모리 관리 체계와는 달리 상당한 노력 없이는 유효하지 않은 참조를 작성할 수 없습니다).
많은 오류를 포착하는 일반적인 솔루션은 포인터를 액세스하기 전에 NULL
(또는 올바른 C ++에서 0
) 아무것도 가리 키지 않는 모든 포인터를 설정하고 포인터를 확인하는 것입니다. 당신이 경우 특히, (당신은 이미 당신이 그들을 선언 할 때 그들을 가리 키도록 뭔가를하지 않는 한) NULL로 모든 포인터를 초기화하고 NULL로 설정하는 것이 일반적이다 delete
또는 free()
그들 (그들은 즉시 그 후 범위 밖으로 이동하지 않는 한). 예 (C에서는 유효하지만 C ++에서도) :
void fill_foo(int* foo) {
*foo = 23; // this will crash and burn if foo is NULL
}
더 나은 버전 :
void fill_foo(int* foo) {
if (!foo) { // this is the NULL check
printf("This is wrong\n");
return;
}
*foo = 23;
}
널 검사가 없으면 NULL 포인터를이 함수에 전달하면 segfault가 발생하고 수행 할 수있는 작업이 없습니다. OS는 단순히 프로세스를 종료하고 코어 덤프 또는 충돌 보고서 대화 상자를 표시합니다. 널 점검을 사용하면 적절한 오류 처리를 수행하고 정상적으로 복구 할 수 있습니다. 문제를 직접 수정하고, 현재 작업을 중단하고, 로그 항목을 작성하고, 사용자에게 알맞게 알리십시오.
다른 답변은 귀하의 정확한 질문에 거의 적용되었습니다. 수신 한 포인터가 실제로 유형의 유효한 인스턴스 (객체, 기본 요소 등)를 가리키는 지 확인하기 위해 널 검사가 수행됩니다.
그래도 여기에 나만의 조언을 추가 할 것입니다. 널 검사를 피하십시오. :) 널 검사 (및 다른 형태의 방어 프로그래밍)는 코드를 어지럽히고 실제로 다른 오류 처리 기술보다 오류가 발생하기 쉽습니다.
객체 포인터와 관련하여 내가 가장 좋아하는 기술은 Null Object 패턴 을 사용하는 것 입니다 . 즉, null 대신 빈 배열 또는 목록을 가리키는 (포인터 또는 더 나은 참조) 또는 null 대신 빈 문자열 ( "") 또는 문자열 "0"(또는 "nothing" 문맥에서 ")를 사용하여 정수로 파싱 할 것으로 예상합니다.
보너스로, 1965 년 CAR Hoare가 Algol W 언어를 위해 (공식적으로) 구현 한 널 포인터에 대해 알지 못했을 것입니다.
나는 그것을 10 억 달러의 실수라고 부릅니다. 그것은 1965 년에 null 참조의 발명이었다. 당시 나는 객체 지향 언어 (ALGOL W)로 참조 할 수있는 최초의 포괄적 인 타입 시스템을 설계하고 있었다. 필자의 목표는 컴파일러가 자동으로 검사를 수행하여 모든 참조 사용이 절대적으로 안전하도록하는 것이 었습니다. 그러나 구현하기가 쉽기 때문에 null 참조를 넣는 유혹에 저항 할 수 없었습니다. 이로 인해 수많은 오류, 취약성 및 시스템 충돌이 발생하여 지난 40 년간 수십억 달러의 고통과 피해를 초래했을 수 있습니다.
널 포인터 값은 잘 정의 된 "nowhere"를 나타냅니다. 다른 포인터 값과 같지 않은 것을 비교할 수 있는 유효하지 않은 포인터 값입니다. 널 포인터의 역 참조를 시도하면 정의되지 않은 동작이 발생하고 일반적으로 런타임 오류가 발생하므로 역 참조를 시도하기 전에 포인터가 NULL이 아닌지 확인하려고합니다. 많은 C 및 C ++ 라이브러리 함수는 오류 조건을 표시하기 위해 널 포인터를 리턴합니다. 예를 들어, 라이브러리 함수 malloc
는 요청 된 바이트 수를 할당 할 수없는 경우 널 포인터 값을 리턴하고 해당 포인터를 통해 메모리에 액세스하려고하면 (일반적으로) 런타임 오류가 발생합니다.
int *p = malloc(sizeof *p * N);
p[0] = ...; // this will (usually) blow up if malloc returned NULL
따라서 NULL malloc
값을 확인하여 호출이 성공 했는지 확인해야합니다 p
.
int *p = malloc(sizeof *p * N);
if (p != NULL) // or just if (p)
p[0] = ...;
자, 양말을 잠시만 기다려주십시오. 약간 울퉁불퉁합니다.
널 포인터 값 과 널 포인터 상수 가 있으며 둘이 반드시 같을 필요는 없습니다. 널 포인터 값 은 기본 아키텍처가 "nowhere"를 나타내는 데 사용하는 값입니다. 이 값은 0x00000000 또는 0xFFFFFFFF 또는 0xDEADBEEF이거나 완전히 다른 값일 수 있습니다. 널 포인터 값 이 항상 0 이라고 가정하지 마십시오 .
널 포인터 상수 OTOH는 항상 0 값의 정수 표현식입니다. 지금까지 당신과 같이 소스 코드를 우려, 0 (또는 통합 식 0 평가는 것을) 널 포인터를 나타냅니다. C와 C ++ 모두 NULL 매크로를 널 포인터 상수로 정의합니다. 코드가 컴파일되면 널 포인터 상수 가 생성 된 기계 코드에서 적절한 널 포인터 값 으로 대체됩니다 .
또한 NULL은 유효하지 않은 많은 포인터 값 중 하나 일뿐입니다 . 명시 적으로 초기화하지 않고 자동 포인터 변수를 선언하는 경우
int *p;
변수에 처음 저장된 값은 미정 이며 유효하거나 액세스 가능한 메모리 주소에 해당하지 않을 수 있습니다. 불행히도, 널 (NULL)이 아닌 포인터 값을 사용하기 전에 유효한지 여부를 알 수있는 (이동식) 방법은 없습니다. 따라서 포인터를 다루는 경우 일반적으로 선언 할 때 명시 적으로 NULL로 초기화하고 적극적으로 무언가를 가리 키지 않을 때 NULL로 설정하는 것이 좋습니다.
이것은 C ++보다 C에서 더 큰 문제입니다. 관용적 C ++은 포인터를 많이 사용해서는 안됩니다.
몇 가지 방법이 있으며 모두 본질적으로 동일한 일을합니다.
int * foo = NULL; // 때때로 NULL 대신 0x00 또는 0 또는 0L로 설정
널 확인 (포인터가 널인지 확인), 버전 A
if (foo == NULL)
널 확인, 버전 B
if (! foo) // NULL이 0으로 정의되었으므로! foo는 널 포인터에서 값을 리턴합니다.
null 확인, 버전 C
if (foo == 0)
세 가지 중에서 첫 번째 검사는 미래의 개발자에게 무엇을 확인하려고하는지 명시 적으로 알려주고 foo가 포인터가 될 것으로 예상한다는 것을 명확하게 알려주는 것을 선호합니다.
당신은하지 않습니다. C ++에서 포인터를 사용하는 유일한 이유는 널 포인터의 존재를 명시 적으로 원하기 때문입니다. 그렇지 않으면 의미 적으로 사용하기 쉽고 null이 아닌 것을 보장하는 참조를 사용할 수 있습니다.
export
)과 모든 C ++ 03 라이브러리 기능 및 TR1 및 C ++ 11의 좋은 덩어리가 포함됩니다.