NULL 값을 0으로 대체 할 수 있습니까?


73

? NULL값의 대체물로 포인터 를 사용할 수 0있습니까?

아니면 그 일에 잘못된 것이 있습니까?


예를 들어,

int i = NULL;

대체품 :

int i = 0;

실험으로 다음 코드를 컴파일했습니다.

#include <stdio.h>

int main(void)
{
    int i = NULL;
    printf("%d",i);

    return 0;
}

산출:

0

실제로 그것은 나에게 완전히 정확한 경고를줍니다.

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 

그러나 결과는 여전히 동일합니다.


  • 이 "정의되지 않은 동작"으로 넘어 가고 있습니까?
  • NULL이런 식 으로 활용하는 것이 허용 됩니까?
  • NULL산술 표현에서 숫자 값 으로 사용 하는 데 문제가 있습니까?
  • 이 경우 C ++의 결과와 동작은 무엇입니까?

나는의 답변 읽게 '\ 0', NULL의 차이점은 무엇과 0 사이의 차이가 무엇인지에 대한 NULL, \0그리고 0그것을 잘 사용도 매우 허용하고 경우입니다,하지만 난 거기에서 간결한 정보를 얻을하지 않았다 NULL등 과제 및 기타 산술 연산에서 사용할 가치.


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Samuel Liew

C와 C ++에 대해 두 가지 별도의 질문을하는 것이 좋습니다.
Konrad Rudolph

답변:


82

NULL 값을 0 값 대신 사용할 수 있습니까?

아닙니다 . 그렇게하는 것은 안전하지 않습니다. NULL은 null 포인터 상수로 type을 가질 int 있지만 더 일반적으로 type void *(C)을 갖거나 그렇지 않으면 int(C ++> = 11)에 직접 할당 할 수 없습니다 . 두 언어 모두 포인터를 정수로 변환 할 수 있지만 이러한 변환을 암시 적으로 수행 할 수는 없습니다 (일부 컴파일러는이를 확장으로 제공하지만). 또한 null 포인터를 정수로 변환하여 값 0을 생성하는 것이 일반적이지만 표준에서는이를 보증하지 않습니다. 유형 int과 값이 0 인 상수를 원하면 철자를 입력하십시오 0.

  • 이것으로 정의되지 않은 동작으로 넘어갈 수 있습니까?

예. NULL유형이 void *있거나 값을 직접 할당 할 수없는 다른 값으로 확장되는 구현에서 그렇습니다 int. 표준은 그러한 구현에 대한 과제의 행동을 정의하지 않으며 행동이 정의되지 않았습니다.

  • 그런 식으로 NULL로 작동하는 것이 허용됩니까?

스타일이 좋지 않으며 일부 시스템 및 일부 환경에서는 중단 될 수 있습니다. GCC를 사용하는 것처럼 옵션을 사용하여 컴파일하면 자체 예제에서 중단됩니다 -Werror.

  • 산술 표현식에서 NULL을 숫자 값으로 사용하는 데 문제가 있습니까?

예. 숫자 값이 전혀 보장되지는 않습니다. 0을 의미하는 경우 0을 작성하십시오. 이는 잘 정의되어있을뿐만 아니라 더 짧고 명확합니다.

  • 그리고 C ++의 결과는 어떻게 그런가요?

C ++ 언어는 C보다 변환에 대해 더 엄격하고에 대한 규칙이 다르지만 NULL구현시 확장을 제공 할 수도 있습니다. 다시 말하지만, 0을 의미한다면 이것이 바로 써야 할 것입니다.


4
"더 일반적으로 유형이있는 유형 void *"은 C에만 해당 된다는 점을 지적해야합니다 . 다른 포인터 유형에 void *할당 할 수 없기 때문에 C ++에는 올바른 유형이 아닙니다 void*. C ++ 89 및 C ++ 03에서는 실제로 유형 NULL 이어야int 하지만 이후 버전에서는 (그리고 일반적으로) 될 수 있습니다 nullptr_t.
Martin Bonner는 Monica

당신은 또한 변환 void*하는 int것이 정의되지 않은 행동 이라고 잘못했습니다 . 그렇지 않습니다. 구현 지정 동작입니다.
Martin Bonner는 Monica

@MartinBonnersMonica, C가 포인터가 정수로 변환되도록 지정하는 문맥에서 변환의 결과는 실제로 구현에 따라 지정되지만 이것이 내가 말하는 것은 아닙니다. 정의되지 않은 동작을 갖는 (캐스트를 통해 명시 적으로 변환하지 않고) 정수 유형의 lvalue에 대한 포인터 할당입니다. 이 언어는 자동 변환을 정의하지 않습니다.
John Bollinger

@MartinBonnersupportsMonica, C ++ 고려 사항을보다 포괄적으로 포함하도록 편집했습니다. 어쨌든 중심 주제는 두 언어 모두에 동일하게 적용됩니다. 정수 0을 원하면이를 적절한 유형의 정수 상수로 명시 적으로 작성하십시오.
John Bollinger

31

NULL널 포인터 상수입니다. C에서는 값이있는 정수 상수 표현식 0이거나 로 변환 된 표현식이 될 수 있습니다 void*. 즉 , 0과 상호 교환 가능 하게 사용할 수 없습니다NULL . 예를 들어이 코드 샘플에서

char const* foo = "bar"; 
foo + 0;

두 포인터 사이의 추가 (다른 포인터 유형은 제외)가 정의되지 않았으므로로 대체 0하는 NULL것이 유효한 C 프로그램이라고 보장되지 않습니다. 제약 조건 위반으로 인해 진단이 발행됩니다. 더하기 피연산자가 유효하지 않습니다 .


C ++의 경우 상황이 약간 다릅니다. void*다른 객체 유형 으로의 암시 적 변환 부족 NULL은 역사적으로 0C ++ 코드에서 와 같이 정의되었습니다 . C ++ 03에서는 아마도 그것을 벗어날 수 있습니다. 그러나 C ++ 11부터는 합법적으로 키워드 로 정의nullptr 할 수 있습니다 . std::nullptr_t포인터 유형에 추가되지 않을 수 있으므로 이제 다시 오류가 발생 합니다.

NULL로 정의 되면 nullptr실험조차도 무효가됩니다. std::nullptr_t정수에서 정수로의 변환은 없습니다 . 이것이 더 안전한 널 포인터 상수로 간주되는 이유입니다.


완벽을 기하기 위해 0L은 널 포인터 상수이기도하며 NULL두 언어 모두에서 사용할 수 있습니다 .
eerorika

1
@jamesqf 표준에 따르면 값이 0 인 정수 상수는 널 포인터 상수입니다. 따라서 0L은 널 포인터 상수입니다.
eerorika

1
@ eerorika : 세상이 필요로하는 것, 현실을 무시하는 표준 :-) '80286 어셈블리를 올바르게 기억한다면 원거리 포인터를 단일 작업으로 할당 할 수 없기 때문에 컴파일러 작성자는 특별해야합니다. -케이스
jamesqf

2
@jamesqf 당 은 C 자주 묻는 질문 , 재 : 만드는 0널 포인터 상수 : "분명히 잘못된 가정 만든 현존하는 잘못 작성된 C 코드 모두에게 SOP로"
앤드류 헨레을

3
@ jamesqf, 값이 0 인 모든 정수 상수는 널 포인터 상수 (C에서)가 하드웨어 포인터 구현과 관련이 없습니다. 표준 C는 어떤 경우에도 근거리 포인터와 원거리 포인터의 구별을 인식하지 않지만 포인터 대 포인터 할당을 지원합니다. 또한 (일부) 포인터 비교를 지원하여 286과 같은 세그먼트 주소 지정 형식에 대한 흥미로운 문제를 제시합니다.
John Bollinger

21

NULL 포인터를 0 값 대신 사용할 수 있습니까?

int i = NULL;

규칙은 언어와 버전에 따라 다릅니다. 어떤 경우에는 할 있고 다른 경우에는 할 수 없습니다. 어쨌든 해서는 안됩니다 . 운이 좋으면, 컴파일러는 시도하거나 더 나아질 때 경고를 표시하고 컴파일에 실패합니다.

C ++에서 C ++ 11 이전 (C ++ 03에서 인용) :

[lib.support.types]

NULL은이 국제 표준에서 구현 정의 된 C ++ 널 포인터 상수입니다.

널 포인터 상수를 정수로 사용하는 것은 의미가 없습니다. 하나...

[conv.ptr]

널 포인터 상수는 0으로 평가되는 정수 유형의 정수 상수 표현식 (5.19) rvalue입니다.

따라서 말이되지 않더라도 기술적으로 작동합니다. 이 기술로 인해 악용되는 프로그램이 잘못 작성 될 수 있습니다 NULL.

C ++ 11 이후 (최신 초안 인용) :

[conv.ptr]

NULL 포인터 상수 값이 0 인 정수 리터럴 ([lex.icon])이다 또는 형식 STD의 prvalue :: nullptr_t .

A std​::​nullptr_­t는 정수로 변환 할 수 없으므로 정수로 사용 NULL하면 언어 구현에서 선택한 사항에 따라 조건부로만 작동합니다.

PS nullptr는 유형의 prvalue입니다 std​::​nullptr_­t. C ++ 11 이전 버전에서 컴파일하기 위해 프로그램이 필요하지 않으면 항상 nullptr대신 대신 사용해야 합니다 NULL.


C는 조금 다릅니다 (C11 초안 N1548에서 인용) :

6.3.2.3 언어 / 변환 / 기타 피연산자 / 포인터

3 값이 0 인 정수 상수 표현식 또는 type으로 변환 된 표현식을void * 널 포인터 상수라고합니다. ...

따라서이 사례는 C ++ 11 이후와 유사하다. 즉 NULL언어 구현에 의해 만들어진 선택에 따라 조건부 로 작업 이 남용 된다.


10

, 구현에 따라 캐스트 필요할 수 있습니다 . 그러나 그렇습니다. 100 % 합법적입니다.

비록 실제로는 정말 나쁜 스타일이지만 (말할 필요는 없습니까?)

NULL표준은 실제로 C ++ 아니 거나 C 였습니다 . 그러나 많은 C 레거시와 마찬가지로 표준 에는 기술적으로 NULLC ++를 만드는 두 개의 절 ([diff.null] 및 [support.types.nullptr])이 있습니다. 그것은이다 구현 정의 널 포인터 상수 . 따라서 스타일이 좋지 않더라도 기술적으로 C ++만큼 가능합니다. 각주
에서 지적한 바와 같이 , 가능한 구현은 또는 가능 하지만 불가능할 수있다 .00L (void*)0

NULL물론 (표준에서는 명시 적으로 말하지는 않지만 0또는 이후에 남아있는 유일한 선택 일 0L수 있음) 수 있습니다 nullptr. 그것은 거의 사실이 아니지만, 법적 가능성입니다.

컴파일러가 사용자에게 표시 한 경고는 컴파일러가 실제로 C 모드로 컴파일되지 않은 경우 호환되지 않음을 나타냅니다. 또한, 경고에 따르면,이 때문에 변환했다 (아니 널 포인터가 nullptr될 것이다 nullptr_t별개의 것), 너무 분명히 정의를 NULL참으로 (void*)0, 그것은하지 않을 수있다.

어느 쪽이든, 당신은 두 가지 가능한 합법적 인 (즉, 컴파일러가 깨지지 않은) 경우가 있습니다. (실제 경우) NULL0or 와 같 0L으며 정수로 "0 또는 1"로 변환되어 있으므로 사용하는 것이 좋습니다.

아니면 NULL실제로 nullptr입니다. 이 경우 당신은 비교뿐만 아니라 명확하게 정의 된 변환에 대한 보장을 가진 별개의 값이 에서 그러나 불행하게도하지, 정수 정수. 그러나로 명확하게 정의 된 변환 bool( false), bool정수로 명확하게 정의 된 변환 ( 0).

불행히도, 이것은 두 번의 변환이므로 [conv]에서 지적한 것처럼 "0 또는 1" 내에 있지 않습니다 . 구현이 정의하는 경우 따라서, NULL같은 nullptr, 당신은 당신의 코드가 정확해야하는 명시 적 캐스트를 추가해야합니다.


6

C FAQ에서 :

Q : NULL과 0이 널 포인터 상수와 같은 경우 어떤 것을 사용해야합니까?

A : 포인터 컨텍스트에서만 존재 NULL하며 0이에 상응합니다. NULL작동 하지 않더라도 다른 종류의 0이 필요할 때는 사용 하지 않아야합니다. 잘못된 스타일 메시지가 전송되기 때문입니다. 또한 ANSI는 NULL의 정의가 ((void *)0)포인터가 아닌 컨텍스트에서는 전혀 작동하지 않는 NULL을 허용합니다 . 특히 NULLASCII 널 문자 (NUL)가 필요한 경우에는 사용하지 마십시오 . 자신의 정의를 제공

http://c-faq.com/null/nullor0.html


5

면책 조항 : 나는 C ++을 모른다. 내 대답은 C ++의 맥락에서 적용되도록 의도되지 않았습니다.

'\0'int단지 100 % 정확히 같은 값을 0으로 0.

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

포인터의 맥락에서 , 0그리고 NULL100 % 동일하다 :

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

100 % 동일합니다.


에 대한 참고 ptr + NULL

문맥은 포인터 의 맥락 ptr + NULL아닙니다 . C 언어로 포인터를 추가하는 것에 대한 정의는 없습니다. 포인터와 정수를 더하거나 뺄 수 있습니다. 에서 ptr + NULL어느 경우 ptr또는 NULL포인터이고, 다른 하나는 해야 하므로, 정수 ptr + NULL효과적이다 (int)ptr + NULL또는 ptr + (int)NULL과의 정의에 따라 ptr그리고 NULL몇 가지 행동을 예상 할 수있다 : 모든 작업, 포인터와 정수, 컴파일 실패의 전환에 대한 경고 .. .


#define NULL (void *)0전에 본 적이 있습니다 . NULL과 평문 0이 100 % 동일합니까?
machine_1

2
포인터의 맥락에서, 그렇습니다 ... 나의 대답에서 강조된 상태, 감사합니다
pmg

@ phuclv : 나는 C ++에 대해 전혀 모른다. 내 대답 (괄호 사이의 비트 제외)은 약 C
pmg입니다.

@phuclv ptr + NULLNULL포인터와 관련 하여 사용하지 않습니다
pmg

3
@JesperJuhl : 포인터와 관련하여 100 % 동일합니다. 내가 무슨 생각이없는 nullptr것입니다,하지만 ((void*)0)0(또는 '\0'...) 포인터의 맥락에서 동일if (ptr == '\0' /* or equivalent 0, NULL */)
PMG

5

더 이상 사용하지 않는 것이 좋습니다 NULL(오래된 포인터 초기화 방법).

C ++ 11부터 :

키워드 nullptr는 포인터 리터럴을 나타냅니다. std :: nullptr_t 유형의 prvalue입니다. nullptr포인터 유형과 멤버 유형에 대한 포인터의 널 포인터 값으로의 암시 적 변환이 있습니다 . null 포인터 상수에 대해서도 유사한 변환이 있으며 여기에는 std::nullptr_t매크로뿐만 아니라 type 값도 포함됩니다 NULL.

https://en.cppreference.com/w/cpp/language/nullptr

실제로 std :: nullptr_t 는 널 포인터 리터럴의 유형입니다 nullptr. 포인터 유형이나 멤버 유형에 대한 포인터가 아닌 고유 한 유형입니다.

#include <cstddef>
#include <iostream>

void f(int* pi)
{
   std::cout << "Pointer to integer overload\n";
}

void f(double* pd)
{
   std::cout << "Pointer to double overload\n";
}

void f(std::nullptr_t nullp)
{
   std::cout << "null pointer overload\n";
}

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(0);  // ambiguous call: all three functions are candidates
    // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                // (as is the case in most implementations)
}

산출:

Pointer to integer overload
Pointer to double overload
null pointer overload

문제는 정수에 NULL을 0에 할당하는 것입니다. 이런 의미에서 NULL 대신 nullptr로 변경되는 것은 없습니다.
ivan.ukr

그는 단어를 "NULL 포인터"로 사용했습니다.
Mannoj

그런데 C ++에는 C ++ 11 이후에 NULL 개념이 없습니다. constexpr 사용에 대해 혼란스러워하거나 구식 초기화를 정의 할 수 있습니다. en.cppreference.com/w/cpp/language/default_initialization
Mannoj
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.