C / C ++에서 NULL 포인터 확인 [닫기]


153

최근 코드 검토에서 기고자는 NULL포인터에 대한 모든 검사가 다음과 같은 방식으로 수행되도록 노력하고 있습니다.

int * some_ptr;
// ...
if (some_ptr == NULL)
{
    // Handle null-pointer error
}
else
{
    // Proceed
}

대신에

int * some_ptr;
// ...
if (some_ptr)
{
    // Proceed
}
else
{
    // Handle null-pointer error
}

나는 "이 포인터가 NULL이 아닌지 확인하십시오"라는 말의 의미에서 그의 방식이 좀 더 명확하다는 데 동의하지만,이 코드를 작업하는 사람은 포인터 변수를 사용하면 if문이 암시 적으로 검사하고 NULL있습니다. 또한 두 번째 방법은 ilk의 버그를 도입 할 가능성이 적습니다.

if (some_ptr = NULL)

그것은 찾아서 디버깅하는 데 절대적으로 고통입니다.

어떤 방법을 선호하고 왜?


32
어떤 스타일을 선택하는 것보다 일관된 스타일을 갖는 것이 때때로 더 중요합니다.
Mark Ransom

15
@ 마크 : 일관성은 항상 스타일의 가장 중요한 요소입니다.
sbi

13
"또한 두 번째 방법은 ilk의 버그를 도입 할 가능성이 적다고 생각합니다. if (some_ptr = NULL)"어떤 사람들은 if (NULL == some_ptr)를 사용하는 이유는 NULL에 할당 할 수 없으므로 컴파일 오류
user122302

14
요즘 대부분의 컴파일러는 조건부 할당에 대해 경고합니다. 불행히도 너무 많은 개발자가 컴파일러의 경고를 무시합니다.
Mike Ellery

10
일관성이 중요하다는 위의 진술에 동의하지 않습니다. 이것은 일관성과 관련이 없습니다. 어쨌든 좋은 정렬이 아닙니다. 프로그래머가 자신의 스타일을 표현할 수있는 영역입니다. 어느 한 형태를 즉시 이해하지 못하는 사람이 있다면 즉시 코드 읽기를 중단하고 경력 변화를 고려해야합니다. 나는 그 이상을 말할 것입니다 : 화장품 일관성이 있으면 스크립트로 자동으로 시행 할 수 없으며 제거하십시오. 인간의 도덕을 빼앗는 데 드는 비용은 사람들이 집회에서하는 어리석은 결정보다 더 중요합니다.
wilhelmtell

답변:


203

내 경험상 양식 if (ptr)또는 시험 if (!ptr)이 선호됩니다. 그것들은 심볼의 정의에 의존하지 않습니다 NULL. 실수로 할당 할 수있는 기회를 제공하지 않습니다. 그리고 그들은 명확하고 간결합니다.

편집 : SoapBox는 주석에서 지적한 auto_ptr것처럼 포인터 역할을하고 bool정확하게이 관용구를 활성화 하기 위해 변환을 제공하는 객체 와 같은 C ++ 클래스와 호환됩니다 . 이러한 개체의 경우 명시 적으로 비교하려면 NULL다른 의미 부작용이 있거나 bool변환이 암시 하는 단순 존재 확인보다 비쌀 수있는 포인터로의 변환을 호출해야 합니다.

불필요한 텍스트가없는 의미를 나타내는 코드를 선호합니다. 중복 특이성 if (ptr != NULL)과 동일한 의미를 지니고 if (ptr)있습니다. 다음 논리는 글을 쓰는 if ((ptr != NULL) == TRUE)것이고, 그런 식으로 광기가 있습니다. C 언어는 부울 테스트 분명하다 if, while나 아닌 값의 의미 특정을 가지고 같은 사실과 제로는 false입니다. 중복성은 명확하지 않습니다.


23
또한 이들은 일반적으로 연산자 bool (또는 safe_bool)을 대체하는 포인터 랩퍼 클래스 (shared_ptr, auto_ptr, scoped_tr 등)와 호환됩니다.
비누 상자

2
토론에 추가하는 auto_ptr에 대한 참조로 인해 이것을 "답변"으로 투표하고 있습니다. 질문을 작성한 후에는 이것이 실제로 다른 것보다 더 주관적이며 아마도 상황에 따라 "올바른"일 수 있기 때문에 stackoverflow "답변"에 적합하지 않을 것입니다.
Bryan Marble

2
@Bryan, 나는 "더 나은"답변이 나오면 필요에 따라 체크 표시를 자유롭게 이동하십시오. 주관성에 관해서는 그렇습니다. 그러나 질문이 제기 될 때 항상 명백하지 않은 객관적인 문제가있는 경우에는 적어도 주관적인 토론입니다. 선이 흐릿합니다. 그리고 주관적인 ... ;-)
RBerteig

1
주목해야 할 한 가지 중요한 사항 : 최근까지는 "if (ptr! = NULL)"또는 "if (ptr! = nullptr)"대신 "if (ptr)"대신 쓰는 것이 더 간결하므로 더 읽기 쉬운 코드. 그러나 최근 컴파일러에서 비교가 NULL / nullptr과 비교할 때 경고 (오류)가 발생한다는 것을 알았습니다. 따라서 포인터를 확인하려면 "if (ptr)"대신 "if (ptr! = NULL)"을 수행하는 것이 좋습니다. 실수로 ptr을 int로 선언하면 첫 번째 검사에서 경고 / 오류가 발생하지만 후자는 경고없이 컴파일됩니다.
Arnaud

1
@David 天宇 Wong 아니오, 그게 아니에요. 해당 페이지의 예 int y = *x; if (!x) {...}는 옵티마이 저가 NULL 인 *x경우 undefined x이므로 NULL이 아니 !x어야하므로 FALSE 여야 하는 이유를 옵티마이 저가 허용 하는 두 행 시퀀스 입니다. 식은 정의 되지 않은 동작 !ptr아닙니다 . 이 예에서를 사용 하기 전에 테스트를 수행 *x한 경우 최적화 프로그램이 테스트를 제거하기 위해 잘못되었습니다.
RBerteig


25

이것부터 시작하겠습니다 : 일관성이 중요합니다. 코드 기반의 일관성보다 결정이 덜 중요합니다.

C ++에서

C ++에서 NULL은 0 또는 0L로 정의됩니다.

당신이 읽은 경우 언어 프로그래밍은 C ++ 사용하여 제안 비얀 스트로브 스트 룹을 0방지하기 위해 명시 적으로 NULL임무를 수행 할 때 매크로를 그가 비교와 동일 한 경우에, 잘 모르겠어요 내가 책을 읽을 수 있기 때문에, 나는 그가 단지했다고 생각하지만, 그것은을이었다 if(some_ptr)명백한 비교가 없지만 나는 그것에 모호합니다.

그 이유는 NULL매크로가 (거의 모든 매크로와 마찬가지로) 기만적이기 때문에 실제로 0문자 그대로이며 이름에서 알 수 있듯이 고유 한 유형이 아닙니다. 매크로를 피하는 것은 C ++의 일반적인 지침 중 하나입니다. 반면에 0정수처럼 보이며 포인터와 비교하거나 포인터에 할당 할 때는 그렇지 않습니다. 개인적으로 나는 어느 쪽이든 갈 수 있지만 일반적으로 나는 명백한 비교를 건너 뜁니다 (어떤 사람들은 이것을 싫어하기 때문에 아마도 변경 사항을 제안하는 기고자가있는 것입니다).

개인적인 감정에 관계없이 이것은 올바른 방법이 없기 때문에 가장 악한 선택입니다.

이것은 명확하고 일반적인 관용구이며 선호합니다. 비교 중에 실수로 값을 할당 할 가능성이 없으며 명확하게 읽습니다.

if(some_ptr){;}

이것이 some_ptr포인터 유형 이라는 것을 알면 분명 하지만 정수 비교처럼 보일 수도 있습니다.

if(some_ptr != 0){;}

이것은 명백한 일이며, 일반적인 경우에 의미 NULL0있습니다.

if(some_ptr != NULL){;}

C ++ 0x에는 명시적이고 정확하기 때문에 선호되는 방법 인 nullptr이 있습니다. 우연한 할당에주의하십시오.

if(some_ptr != nullptr){;}

C ++ 0x로 마이그레이션 할 때까지 사용하는 방법 중 하나를 걱정하는 데 시간 낭비라고 주장 할 수 있습니다. 완전히 전달 된 일반적인 프로그래밍 문제와 함께 nullptr이 발명 된 이유는 모두 충분하지 않습니다 .) 가장 중요한 것은 일관성을 유지하는 것입니다.

C에서

C는 다른 짐승입니다.

C NULL에서 0 또는 ((void *) 0)으로 정의 될 수있는 C99는 구현 정의 널 포인터 상수를 허용합니다. 따라서 실제로 구현의 NULL 정의로 귀결되며 표준 라이브러리에서 검사해야합니다.

매크로는 매우 일반적이며 일반적으로 언어 및 기타 사항에서 일반 프로그래밍 지원의 결함을 보완하기 위해 많이 사용됩니다. 언어는 훨씬 단순하고 전처리기에 더 많이 의존합니다.

이 관점 NULL에서 C 에서 매크로 정의를 사용하는 것이 좋습니다 .


tl; dr와 0C ++에서는 옳지 만 C :에는 의미가 (void *)0있습니다. 형식 오류는 PITA 일 수 있으므로 C ++에서 0선호됩니다 NULL.
Matt Joiner

@ 매트 : 그러나 NULL어쨌든 제로입니다.
GManNickG

@GMan : 안 내 시스템에 : #define NULL ((void *)0)에서linux/stddef.h
매트 소목

@Matt : C ++에서. 당신은 0이 바람직하다고 말했지만 NULL0이어야합니다.
GManNickG

이것은 좋은 지적입니다. 나는 그것을 언급하지 않았고 그것을 제안함으로써 대답을 향상시키고 있습니다. ANSI C는 ((void *) 0)으로 정의 된 NULL을 가질 수 있으며, C ++는 NULL을 0으로 정의합니다.이 표준을 직접 트래킹하지는 않았지만 C ++에서는 NULL이 0 또는 0L 일 수 있다는 것을 이해합니다.
M2tM

19

나는을 사용 if (ptr)하지만 이것에 대해 논쟁 할 가치가 전혀 없다.

간결하기 때문에 내 방식이 마음에 들지만 다른 사람들 == NULL은 읽기 쉽고 명확하게 말합니다 . 나는 그들이 어디에서 왔는지 알았습니다. 여분의 물건에 동의하지 않기 때문에 더 쉽습니다. (매크로가 싫기 때문에 편견이 있습니다.) 당신에게 달려 있습니다.

나는 당신의 주장에 동의하지 않습니다. 조건부에서 할당에 대한 경고가 표시되지 않으면 경고 수준을 높여야합니다. 그렇게 간단합니다. (그리고 모든 것을 사랑하기 위해 그것들을 바꾸지 마십시오.)

C ++ 0X에 참고, 우리는 할 수있는 if (ptr == nullptr)나에게있는 않습니다 읽기 좋네요을. (다시 말하지만, 나는 매크로를 싫어합니다. 그러나 nullptr좋습니다.) 그래도 나는 if (ptr)그것이 익숙한 이유 때문에 여전히 그렇습니다.


만약 C ++ / C에 if_exist ()와 같은 연산자가 있다면 5 년 이상의 경험이 당신의 마음을 바꿨기를 바랍니다.
magulla

10

솔직히, 왜 중요한지 모르겠습니다. 어느 쪽이든 분명하고 C 또는 C ++에 대해 어느 정도 경험이있는 사람은 둘 다 이해해야합니다. 그러나 한 가지 의견 :

오류를 인식하고 함수를 계속 실행하지 않으려는 경우 (예 : 예외를 발생 시키거나 오류 코드를 즉시 반환하려는 경우) 가드 절로 만들어야합니다.

int f(void* p)
{
    if (!p) { return -1; }

    // p is not null
    return 0;
}

이렇게하면 "화살표 코드"를 피할 수 있습니다.


누군가가 여기 kukuruku.co/hub/programming/i-do-not-know-c 를 지적했습니다 if(!p). 정의되지 않은 동작입니다. 작동하거나 작동하지 않을 수 있습니다.
David 天宇 Wong

@David 天宇 링크에서 예제 # 2에 대해 이야기하고 있다면 if(!p)정의되지 않은 동작이 아니며 그 이전 줄입니다. 그리고 if(p==NULL)똑같은 문제가 있습니다.
마크 랜섬

8

개인적으로 저는 항상 if (ptr == NULL)의도를 밝히기 때문에 항상 사용해 왔지만이 시점에서는 습관 일뿐입니다.

=대신 ==적절한 경고 설정을 가진 적격 컴파일러가이를 대신 하여 사용 합니다.

중요한 점은 그룹에 일관된 스타일을 선택하고 고수하는 것입니다. 어떤 방법을 사용하든 결국에는 익숙해지며 다른 사람의 코드에서 작업 할 때 마찰이 사라지는 것을 환영합니다.


7

foo == NULL연습 에 찬성하여 한 가지만 더 : foo예를 들어, a int *또는 a bool *인 경우, if (foo)수표는 실수로 포인트의 가치를 테스트하는 것으로 해석 될 수 있습니다 if (*foo). 여기서 NULL비교는 우리가 포인터에 대해 이야기하고 있음을 상기시킵니다.

그러나 나는 좋은 명명 규칙 이이 논쟁을 무시한다고 생각합니다.


4

사실, 나는 두 변종을 모두 사용합니다.

포인터의 유효성을 먼저 확인하고 NULL 인 경우 함수에서 반환 / 종료하는 상황이 있습니다. (이것은 토론에 "기능에 하나의 종료 점이 있어야 함"으로 이어질 수 있음을 알고 있습니다

대부분의 경우 포인터를 확인한 다음 원하는 것을 수행 한 다음 오류 사례를 해결합니다. 결과는 여러 if를 가진 못생긴 x 배 들여 쓰기 코드 일 수 있습니다.


1
나는 하나의 종료점을 사용하여 발생하는 화살표 코드를 개인적으로 싫어합니다. 이미 답을 알고 있다면 종료하지 않는 이유는 무엇입니까?
ruslik

1
@ruslik : C (또는 C-with-classes 스타일 C ++)에서 여러 반환 값을 사용하면 정리가 더 어려워집니다. "실제"C ++에서는 정리가 RAII에 의해 처리되기 때문에 분명히 문제가되지 않지만, 일부 프로그래머는 과거에 갇혀 있고 (거의 유효한 이유 때문에) RAII를 거부하거나 허용하지 않습니다. .
jalf

이러한 경우 @jalf는 goto매우 유용 할 수 있습니다. 또한 대부분의 경우 malloc()정리가 필요한 항목을 더 빠르게 대체 할 수 있습니다 alloca(). 나는 그들이 권장하지는 않는다는 것을 알고 있지만 이유가 있습니다 (나에게 잘 알려진 레이블 goto은 난독 화 된 if/else나무 보다 훨씬 깨끗 합니다).
ruslik

1
alloca비표준이며 완전히 안전하지 않습니다. 실패하면 프로그램이 종료됩니다. 복구 가능성이 없으며 스택이 상대적으로 작기 때문에 오류가 발생할 가능성이 있습니다. 실패하면 힙을 클로버하고 권한을 침해하는 취약점이 발생할 수 있습니다. 할당 될 크기에 작은 경계 alloca가 없으면 사용 하거나 vlas하지 마십시오 (그리고 정상적인 배열을 사용할 수도 있습니다).
R .. GitHub 중지 지원 얼음

4

C 프로그래밍 언어 (K & R)는 실수로 할당을 피하기 위해 null == ptr을 확인하도록합니다.


23
K & R은 또한 "스마트 포인터 란 무엇인가?"라고 물을 것입니다. C ++ 세계에서 상황이 변하고 있습니다.
Alex Emelianov

2
또는 오히려, C ++ 세계에서 상황이 이미 바뀌었다 ";)
jalf

3
K & R은 어디에 (ref)라고 말합니까? 그들은이 스타일을 스스로 사용하지 않습니다. K & R2 2 장에서도 if (!valid)위의 내용을 권장 if (valid == 0합니다.
schot

6
정착하세요. 그는 K & R이 아니라 K & R이라고 말했다. 초기 이름이 대문자로 표시되지 않기 때문에 분명히 유명하지 않습니다.
jmucchiello

1
대문자를 사용하면 속도가 느려집니다
Derek

3

스타일과 형식이 리뷰의 일부가 될 경우, 합의 된 스타일 가이드가 있어야합니다. 있다면 스타일 가이드가 말한대로하십시오. 없는 경우 이와 같은 세부 정보를 기록 할 때 그대로 두어야합니다. 시간과 에너지를 낭비하고 코드 검토가 실제로 밝혀야 할 내용을 혼란스럽게 만듭니다. 스타일 가이드가 없으면 내가 선호하는 규칙을 사용하지 않더라도 이와 같은 코드를 원칙적으로 변경하지 말아야합니다.

그리고 그것이 중요하지는 않지만 제 개인적 취향은 if (ptr)입니다. 그 의미는 심지어 나보다 더 분명하다 if (ptr == NULL).

어쩌면 그는 행복한 길을 가기 전에 오류 조건을 처리하는 것이 더 낫다고 말하려고합니까? 이 경우에도 여전히 리뷰어에 동의하지 않습니다. 나는 이것에 대해 받아 들여진 협약이 있다는 것을 모른다. 그러나 내 의견으로는 가장 "정상적인"조건이 if 서술문에서 가장 먼저 나와야한다. 그렇게하면 함수의 모든 기능과 작동 방식을 파악하기 위해 파고 들지 않았습니다.

예외로 인해 오류로 인해 함수에서 보석이 나오거나 계속 진행하기 전에 복구 할 수 있습니다. 이 경우 먼저 오류를 처리합니다.

if (error_condition)
  bail_or_fix();
  return if not fixed;

// If I'm still here, I'm on the happy path

비정상적인 상태를 사전에 처리함으로써 처리 할 수 ​​있습니다. 내가 앞을 그것을 처리하여 행복한 길을 다시 얻을 수없는 경우에, 다음은 처리되어야 코드를 이해하기 쉽기 때문에 주요 사례 . 내 의견으로는.

그러나 그것이 스타일 가이드에 없다면 그것은 단지 내 의견이며, 당신의 의견은 똑같이 유효합니다. 표준화하거나 그렇지 않습니다. 리뷰어가 의견을 얻었 기 때문에 의사 표준화를하지 마십시오.


1

이것은 포인터 bool가 C ++ 및 C에서 제어 표현식으로 사용할 수있는 유형 및 값으로 평가되는 두 언어의 기본 중 하나입니다 int. 그냥 사용하십시오.


1
  • 포인터는 부울이 아닙니다
  • 최신 C / C ++ 컴파일러는 if (foo = bar)실수로 쓸 때 경고를 표시합니다 .

따라서 나는 선호

if (foo == NULL)
{
    // null case
}
else
{
    // non null case
}

또는

if (foo != NULL)
{
    // non null case
}
else
{
    // null case
}

그러나 일련의 스타일 지침을 작성하는 경우 이와 같은 것을 넣지 않을 것입니다.

포인터에서 널 검사를 수행하십시오.


2
포인터가 부울이 아니라는 것이 사실이지만 C에서는 if 문이 부울을 사용하지 않습니다. 정수 식을 사용합니다.
Ken

@Ken : C가 그런 점에서 깨 졌기 때문입니다. 개념적으로, 그것은 부울 표현이며 (제 생각에) 그렇게 취급되어야합니다.
JeremyP

1
일부 언어에는 null / not null 만 테스트하는 if 문이 있습니다. 일부 언어에는 부호에 대한 정수만 테스트하는 if 문이 있습니다 (3 방향 테스트). 나는 C가 "깨진"것을 당신이 좋아하는 다른 개념을 선택했기 때문에 고려할 이유가 없다. 내가 C에 대해 싫어하는 것이 많이 있지만 C 프로그램 모델이 내 정신 모델과 같지 않다는 것을 의미합니다.
Ken

@Ken : 부울은 개념적 으로 숫자 나 포인터가 아니므로 어떤 언어를 사용 하든 상관 없습니다 .
JeremyP

1
부울이 숫자 또는 포인터라고 말하지 않았습니다. 나는 if 문이 부울 식을 가져야 만하거나 할 수 있다고 주장 할 이유가 없다고 말했고, 현재의 것 외에도 반대 예를 제시했다. 많은 언어는 C의 "제로 / 제로"방식이 아닌 부울 이외의 것을 취합니다. 컴퓨팅에서 부울을 허용하는 (만) if 문을 갖는 것은 비교적 최근의 개발입니다.
Ken

1

나는 C / C ++의 부울의 조건 유형을 확인하지 않는다는 사실의 엄청난 팬이에요 if, for그리고 while문. 나는 항상 다음을 사용합니다.

if (ptr)

if (!ptr)

bool로 변환되는 정수 또는 다른 유형에서도 :

while(i--)
{
    // Something to do i times
}

while(cin >> a >> b)
{
    // Do something while you've input
}

이 스타일의 코딩은 더 읽기 쉽고 명확합니다. 내 개인적인 의견.

최근 OKI 431 마이크로 컨트롤러에서 작업하는 동안 다음과 같은 사실을 발견했습니다.

unsigned char chx;

if (chx) // ...

보다 효율적이다

if (chx == 1) // ...

나중에 컴파일러가 chx의 값을 1과 비교해야하기 때문에 chx는 true / false 플래그 일뿐입니다.


1

내가 사용한 대부분의 컴파일러는 if추가 구문 설탕없이 할당에 대해 경고 할 것이므로 그 주장을 사지 않습니다. 즉, 나는 전문적으로 사용하고 둘 중 어느 것도 선호하지 않습니다. 는 == NULL내 의견에 불구하고 확실히 명확하다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.