printf의 %s 지정자를 사용하여 NULL을 인쇄하는 동작은 무엇입니까?
흥미로운 인터뷰 질문을 받았습니다.
test 1:
printf("test %s\n", NULL);
printf("test %s\n", NULL);
prints:
test (null)
test (null)
test 2:
printf("%s\n", NULL);
printf("%s\n", NULL);
prints
Segmentation fault (core dumped)
시스템에 따라서는 정상적으로 동작하는 경우도 있습니다만, 적어도 세그멘테이션의 장해가 발생하고 있습니다.이 행동에 대한 가장 좋은 설명은 무엇입니까?위의 코드는 C입니다.
다음은 제 gcc 정보입니다.
deep@deep:~$ gcc --version
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
가장 중요한 것:printf
는 %s 인수에 유효한 (NULL이 아닌) 포인터를 요구하고 있기 때문에 NULL을 전달하는 것은 정식으로 정의되어 있지 않습니다.ANSI에 관한 한 올바른 동작입니다(적어도 Harbison과 Steel은 그렇게 말합니다).
그래, 이건 정말 이상한 행동이야알고보니, 지금 일어나고 있는 일은 당신이 단순하게printf
다음과 같습니다.
printf("%s\n", NULL);
gcc는 이것을 디컨스트럭트 해제하기 위한 콜로서 충분히 스마트합니다.puts
첫 번째printf
, 이것은 다음과 같습니다.
printf("test %s\n", NULL);
gcc가 대신 실제 콜을 발신할 정도로 복잡합니다.printf
.
(gcc가 무효에 대해 경고를 보내는 것에 주의해 주세요.printf
인수를 지정합니다.오래전에 파싱 능력을 개발했기 때문입니다*printf
형식을 지정합니다).
이 내용은 를 사용하여 컴파일함으로써 확인할 수 있습니다.-save-temps
옵션을 선택하고 그 결과를 확인합니다..s
파일.
첫 번째 예시를 컴파일 했을 때 다음과 같은 결과가 나왔습니다.
movl $.LC0, %eax
movl $0, %esi
movq %rax, %rdi
movl $0, %eax
call printf ; <-- Actually calls printf!
(댓글은 제가 추가했습니다.)
하지만 두 번째는 이 코드를 생성했습니다.
movl $0, %edi ; Stores NULL in the puts argument list
call puts ; Calls puts
더 이상한 것은 다음과 같은 줄바꿈이 인쇄되지 않는다는 것입니다.마치 이것이 세그먼트 폴트의 원인이 되는 것을 알게 된 것 같기 때문에 문제가 되지 않습니다.(컴파일할 때 경고해 주었습니다)
C언어에 관한 한, 그 이유는 정의되지 않은 동작을 호출하고 있기 때문에 어떤 일이든 일어날 수 있기 때문입니다.
이러한 현상이 발생하는 이유에 대한 메커니즘에 대해 말하자면, 최신 gcc는printf("%s\n", x)
로.puts(x)
,그리고.puts
인쇄하기 위한 바보 같은 코드가 없습니다.(null)
null 포인터를 검출했을 경우, 한편 일반적인 실장은printf
특별한 케이스가 있어요gcc는 (일반적으로) 단순하지 않은 형식의 문자열을 최적화할 수 없기 때문에printf
형식 문자열에 다른 텍스트가 있을 때 실제로 호출됩니다.
섹션 7.1.4(C99 또는 C11의)에서는 다음과 같이 기술되어 있습니다.
§ 7.1.4 라이브러리 기능 사용
1 다음에 나오는 상세한 설명에 달리 명시되지 않는 한 다음 각 문항이 적용된다.함수에 대한 인수에 잘못된 값(함수의 도메인 외부의 값, 프로그램의 주소 공간 외부의 포인터, 대응하는 파라미터가 일정하지 않은 경우 늘 포인터 또는 수정 불가능한 저장소로 가는 포인터 등) 또는 변수 번호를 가진 함수에 의해 예상되지 않는 유형(업그레이드 후)이 있는 경우인수의 동작은 정의되어 있지 않습니다.
「 」의 printf()
에서는 늘 때 알 수 .%s
에 의해 해 주세요.%p
지정자는 정의되지 않은 동작이 아닙니다.)
'요.fprintf()
C2011-C1999 른른 :
§ 7.21.6.1 fprintf 함수
s
'아니오'의 경우l
경우 첫 ] 。、 、
l
길이 수식자가 존재하며 인수는 wchar_t 유형 배열의 초기 요소에 대한 포인터여야 합니다.
p
논쟁은 무효의 포인터가 되어야 한다.포인터의 값은 구현 정의 방식으로 일련의 인쇄 문자로 변환됩니다.
의 s
변환 지정자는 늘 포인터가 적절한 유형의 배열의 초기 요소를 가리키지 않기 때문에 늘 포인터가 유효할 가능성을 배제합니다.의 p
변환 지정자는 특별히 어떤 것을 가리키기 위해 void 포인터가 필요하지 않으므로 NULL은 유효합니다.
많은 구현에서 다음과 같은 문자열이 인쇄된다는 사실(null)
Null 포인터가 전달되면 신뢰하기 위험한 친절이 됩니다.정의되지 않은 동작의 장점은 이러한 응답이 허용되지만 필수는 아니라는 것입니다.마찬가지로 크래시는 허용되지만 필수는 아닙니다(더 안타까운 것은 사람들이 용서하는 시스템에서 일하고 그 후 용서하지 않는 다른 시스템으로 옮겨간다는 것입니다).
NULL
포인터는 어떤 주소도 가리키지 않으며, 이 주소를 인쇄하려고 하면 정의되지 않은 동작이 발생합니다.정의되지 않음은 NULL을 인쇄하려고 할 때 수행할 작업을 컴파일러 또는 C 라이브러리가 결정하는 것을 의미합니다.
언급URL : https://stackoverflow.com/questions/11589342/what-is-the-behavior-of-printing-null-with-printfs-s-specifier
'sourcecode' 카테고리의 다른 글
Vue.js를 사용하여 클릭 시 특정 하위 요소를 업데이트하려면 어떻게 해야 합니까? (0) | 2022.08.30 |
---|---|
오류: Java 가상 시스템 Mac OSX Mavericks를 생성할 수 없습니다. (0) | 2022.08.30 |
알고리즘: 어레이에서 중복 정수를 효율적으로 삭제하는 방법 (0) | 2022.08.30 |
Vue.js 라이브러리를 가져오는 것과 Vue-CLI를 통해 설치하는 것의 차이점은 무엇입니까? (0) | 2022.08.30 |
String Builder에 새 줄을 추가하는 방법 (0) | 2022.08.30 |