IT story

% p로 널 포인터를 인쇄하는 것은 정의되지 않은 동작입니까?

hot-time 2020. 9. 1. 08:02
반응형

% p로 널 포인터를 인쇄하는 것은 정의되지 않은 동작입니까?


%p변환 지정자로 널 포인터를 인쇄하는 것이 정의되지 않은 동작 입니까?

#include <stdio.h>

int main(void) {
    void *p = NULL;

    printf("%p", p);

    return 0;
}

질문은 C 구현이 아닌 C 표준에 적용됩니다.


이것은 우리가 영어의 한계와 표준의 일관되지 않은 구조의 영향을받는 이상한 코너 사례 중 하나입니다. 그래서 기껏해야 증명할없기 때문에 설득력있는 반론을 할 있습니다. :) 1


질문의 코드는 잘 정의 된 동작을 보여줍니다.

[7.1.4]를 질문의 기초이며,의가 시작하자 :

다음의 각 설명은 다음 세부 설명에서 명시 적으로 달리 명시되지 않는 한 적용됩니다. 함수에 대한 인수에 유효하지 않은 값 ( 예 : 함수 도메인 외부의 값 또는 프로그램의 주소 공간 외부에있는 포인터)이있는 경우 또는 널 포인터 , [... 기타 예 ...] ) [...] 동작이 정의되지 않았습니다. [... 기타 진술 ...]

서투른 언어입니다. 한 가지 해석은 개별 설명에 의해 재정의되지 않는 한 목록의 항목이 모든 라이브러리 기능에 대해 UB라는 것입니다. 그러나 목록은 "예를 들어"로 시작하여 완전한 것이 아니라 예시임을 나타냅니다. 예를 들어, 문자열의 올바른 null 종료를 언급하지 않습니다 (예 :의 동작에 중요 strcpy).

따라서 7.1.4의 의도 / 범위는 단순히 "잘못된 값"이 UB로 연결된다는 것입니다 ( 달리 명시되지 않는 한 ). "잘못된 값"으로 간주되는 항목을 확인하려면 각 함수의 설명을 확인해야합니다.

예 1- strcpy

[7.21.2.3] 은 다음과 같이 말합니다.

strcpy함수는가 가리키는 문자열 s2(종료 널 문자 포함)을가 가리키는 배열에 복사합니다 s1. 겹치는 객체간에 복사가 발생하면 동작이 정의되지 않습니다.

널 포인터를 명시 적으로 언급하지 않지만 널 종료 자에 대해서도 언급하지 않습니다. 대신, "가 가리키는 문자열 s2"에서 유일하게 유효한 값이 문자열 (즉, 널로 끝나는 문자 배열에 대한 포인터)임을 유추 합니다.

실제로이 패턴은 개별 설명 전체에서 볼 수 있습니다. 몇 가지 다른 예 :

  • [7.6.4.1는 (fenv)] 에서 현재 부동 소수점 저장 환경 에 뾰족한 물체 에 의해envp

  • [7.12.6.4 (frexp와는)] INT의 정수의 저장 을 뾰족한 작성자exp

  • [7.19.5.1 (FCLOSE)]을 스트림을 지적 으로stream

예 2- printf

[7.19.6.1] 은 다음에 대해 이렇게 말합니다 %p.

p-인수는에 대한 포인터 여야합니다 void. 포인터의 값은 구현 정의 방식으로 일련의 인쇄 문자로 변환됩니다.

Null은 유효한 포인터 값이며이 섹션에서는 null이 특별한 경우이거나 포인터가 개체를 가리켜 야한다는 명시적인 언급을하지 않습니다. 따라서 그것은 정의 된 행동입니다.


1. 표준 작성자가 제시하지 않는 한 또는 상황을 명확히 하는 근거 문서 와 유사한 것을 찾을 수없는 경우 .


짧은 답변

. %p변환 지정자를 사용하여 널 포인터를 인쇄하면 동작이 정의되지 않습니다. 그렇긴하지만 오작동하는 기존 준수 구현을 알지 못합니다.

대답은 모든 C 표준 (C89 / C99 / C11)에 적용됩니다.


긴 답변

%p무효로 유형 포인터의 인수를 기대 지정 변환, 인쇄 가능한 문자에 대한 포인터의 변환은 구현 정의이다. 널 포인터가 예상된다는 것을 나타내지 않습니다.

표준 라이브러리 함수에 대한 소개에서는 (표준 라이브러리) 함수에 대한 인수로서의 널 포인터가 명시 적으로 달리 명시되지 않는 한 유효하지 않은 값으로 간주됨을 명시합니다.

C99 / C11 §7.1.4 p1

[...] 함수에 대한 인수에 유효하지 않은 값 (예 : [...] 널 포인터, [...])이있는 경우 동작이 정의되지 않습니다.

유효한 인수로 널 포인터를 예상하는 (표준 라이브러리) 함수의 예 :

  • fflush() "모든 스트림"(적용되는)을 플러시하기 위해 널 포인터를 사용합니다.
  • freopen() 스트림과 "현재 연관된"파일을 나타내는 데 널 포인터를 사용합니다.
  • snprintf() 'n'이 0 일 때 널 포인터를 전달할 수 있습니다.
  • realloc() 새 객체를 할당하기 위해 널 포인터를 사용합니다.
  • free() 널 포인터를 전달할 수 있습니다.
  • strtok() 후속 호출에 널 포인터를 사용합니다.

에 대한 경우를 취 snprintf()하면 'n'이 0 일 때 널 포인터 전달을 허용하는 것이 합리적이지만 유사한 0 'n'을 허용하는 다른 (표준 라이브러리) 함수의 경우에는 해당되지 않습니다. 예를 들면 : memcpy(), memmove(), strncpy(), memset(), memcmp().

표준 라이브러리 소개뿐만 아니라 다음 함수 소개에서도 다시 한 번 지정됩니다.

C99 §7.21.1 p2 / C11 §7.24.1 p2

Where an argument declared as size_t n specifies the length of the array for a function, n can have the value zero on a call to that function. Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values as described in 7.1.4.


Is it intentional?

I don't know whether the UB of %p with a null pointer is in fact intentional, but since the standard explicitly states that null pointers are considered invalid values as arguments to standard library functions, and then it goes and explicitly specifies the cases where a null pointer is a valid argument (snprintf, free, etc), and then it goes and once again repeats the requirement for the arguments to be valid even in zero 'n' cases (memcpy, memmove, memset), then I think it's reasonable to assume that the C standards committee isn't too concerned with having such things undefined.


The authors of the C Standard made no effort to exhaustively list all of the behavioral requirements an implementation must meet to be suitable for any particular purpose. Instead, they expected that people writing compilers would exercise a certain amount of common sense whether the Standard requires it or not.

The question of whether something invokes UB is seldom in and of itself useful. The real questions of importance are:

  1. Should someone who is trying to write a quality compiler make it behave in predictable fashion? For the described scenario the answer is clearly yes.

  2. Should programmers be entitled to expect that quality compilers for anything resembling normal platforms will behave in predictable fashion? In the described scenario, I would say the answer is yes.

  3. Might some obtuse compiler writers stretch the interpretation of the Standard so as to justify doing something weird? I would hope not, but wouldn't rule it out.

  4. Should sanitizing compilers squawk about the behavior? That would depend upon the paranoia level of their users; a sanitizing compiler probably shouldn't default to squawking about such behavior, but perhaps provide a configuration option to do in case programs might be ported to "clever"/dumb compilers that behave weirdly.

If a reasonable interpretation of the Standard would imply a behavior is defined, but some compiler writers stretch the interpretation to justify doing otherwise, does it really matter what the Standard says?

참고URL : https://stackoverflow.com/questions/44996471/printing-null-pointers-with-p-is-undefined-behavior

반응형