포인터 또는 주소를 인쇄하기위한 올바른 형식 지정자?


194

변수의 주소를 인쇄하기 위해 어떤 형식 지정자를 사용해야합니까? 나는 아래 랏 사이에 혼란스러워합니다.

% u-부호없는 정수

% x-16 진수 값

% p-무효 포인터

주소를 인쇄하기에 가장 적합한 형식은 무엇입니까?

답변:


240

서로 다른 플랫폼 간의 형식 및 변형에 신경 쓰지 않는다고 가정하는 가장 간단한 대답은 표준 %p표기법입니다.

C99 표준 (ISO / IEC 9899 : 1999)은 §7.19.6.1 ¶8에서 말합니다.

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

(C11 — ISO / IEC 9899 : 2011 — 정보는 §7.21.6.1 ¶8에 있습니다.)

일부 플랫폼에서는 선행 0x및 다른 플랫폼에는 포함 되지 않으며 문자는 소문자 또는 대문자 일 수 있으며 C 표준은 내가 알고 있지만 16 진수 출력이라고 정의하지도 않습니다. 그렇지 않은 경우 구현이 없습니다.

포인터를 (void *)캐스트 로 명시 적으로 변환해야하는지 여부는 토론하기에 다소 개방적 입니다. 그것은 명백하고 일반적으로 좋으며 (따라서 내가하는 일) 표준은 '논쟁은 포인터가 될 것입니다'라고 말합니다 void. 대부분의 컴퓨터에서는 명시 적 캐스트를 생략합니다. 그러나 char *주어진 메모리 위치 에 대한 주소 의 비트 표현 이 동일한 메모리 위치에 대한 ' 다른 포인터 '주소와 다른 머신에서는 중요합니다 . 이것은 바이트 주소 지정된 기계 대신 단어 주소 지정된 시스템입니다. 이러한 기계는 요즘 일반적이지 않지만 (아마도 가능하지는 않지만) 대학 이후 처음으로 일한 기계는 그러한 기계 중 하나였습니다 (ICL Perq).

의 구현 정의 동작에 만족하지 않으면 %pC99 <inttypes.h>uintptr_t대신 사용하십시오.

printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);

이를 통해 자신에게 맞게 표현을 미세 조정할 수 있습니다. 16 진수를 대문자로 선택하여 숫자가 균일하게 동일한 높이를 가지므로 시작 부분의 특성 딥이 0xA1B2CDEF나타나므로 0xa1b2cdef숫자를 따라 위아래로 떨어지는 것이 아닙니다 . 그러나 귀하의 선택은 매우 넓은 범위 내입니다. (uintptr_t)이 컴파일시 형식 문자열을 읽을 수있을 때 캐스트는 명확 GCC에 의해 권장합니다. 나는 캐스트를 요청하는 것이 맞다고 생각하지만, 경고를 무시하고 대부분의 시간을 멀리하는 사람들이 있다고 확신합니다.


Kerrek은 다음과 같이 의견을 묻습니다.

나는 표준 프로모션과 variadic 논쟁에 대해 약간 혼란스러워합니다. 모든 포인터가 표준 프로모션으로 무효화됩니까? 그렇지 않으면 int*2 바이트이고 void*4 바이트라면 인수에서 4 바이트를 읽는 것이 분명히 오류일까요?

나는 C 표준은 모든 개체 포인터 그래서, 같은 크기해야한다고 말한다 착각이었다 void *int *다른 크기가 될 수 없습니다. 그러나 C99 표준의 관련 섹션이 그렇게 강조되지 않는다고 생각합니다 (내가 제안한 것이 사실 인 구현에 대해서는 알지 못하지만) :

§6.2.5 유형

¶26 void에 대한 포인터는 문자 유형에 대한 포인터와 동일한 표현 및 정렬 요구 사항을 가져야합니다. 39) 마찬가지로, 적격 또는 비 적격 버전의 호환 가능한 유형에 대한 포인터는 동일한 표현 및 정렬 요구 사항을 가져야한다. 구조 유형에 대한 모든 포인터는 서로 동일한 표현 및 정렬 요구 사항을 가져야합니다. 공용체 유형에 대한 모든 포인터는 서로 동일한 표현 및 정렬 요구 사항을 가져야합니다. 다른 유형에 대한 포인터는 동일한 표현 또는 정렬 요구 사항을 가질 필요는 없습니다.

39) 동일한 표현 및 정렬 요구 사항은 함수에 대한 인수, 함수의 값 반환 및 공용체 멤버와의 호환성을 의미합니다.

(C11은 섹션 §6.2.5, ¶28 및 각주 48에서 동일하게 표시됩니다.)

따라서 포인터가 가리키는 구조가 다른 정렬 요구 사항을 가질 수 있지만 구조에 대한 모든 포인터는 서로 같은 크기 여야하며 동일한 정렬 요구 사항을 공유해야합니다. 노조도 마찬가지입니다. 문자 포인터와 공백 포인터는 크기 및 정렬 요구 사항이 동일해야합니다. 변형 int(의미 unsigned int및 의미 signed int) 에 대한 포인터 는 서로 동일한 크기 및 정렬 요구 사항을 가져야합니다. 다른 유형의 경우에도 유사합니다. 그러나 C 표준은 공식적으로 그렇게 말하지 않습니다 sizeof(int *) == sizeof(void *). 잘, 그래서 당신은 당신의 가정을 검사하는 데 좋습니다.

C 표준에서는 함수 포인터가 객체 포인터와 동일한 크기 일 필요는 없습니다. 그것은 DOS와 같은 시스템에서 다른 메모리 모델을 깰 필요가 없었습니다. 거기에는 16 비트 데이터 포인터가 있지만 32 비트 함수 포인터가있을 수 있습니다. 그렇기 때문에 C 표준에서는 함수 포인터를 객체 포인터로 변환하거나 그 반대로 변환 할 수 없습니다.

다행스럽게도 (POSIX를 대상으로하는 프로그래머의 경우) POSIX는 위반에 들어서고 함수 포인터와 데이터 포인터의 크기가 같아야합니다.

§2.12.3 포인터 유형

모든 함수 포인터 유형은 void에 대한 유형 포인터와 동일한 표현을 가져야합니다. 함수 포인터를 변환 void *하여 표현을 변경해서는 안됩니다. void *이러한 변환의 결과 값은 정보의 손실없이 명시 캐스트를 사용하여 원래의 함수 포인터 형식으로 변환 뒷면 일 수있다.

참고 : ISO C 표준에서는이를 요구하지 않지만 POSIX 준수에 필요합니다.

따라서 void *와 같은 variadic 함수에 대한 포인터를 전달할 때 코드의 최대 안정성을 위해 명시 적 캐스트 가 강력하게 권장됩니다 printf(). POSIX 시스템에서는 인쇄를 위해 함수 포인터를 빈 포인터로 캐스트하는 것이 안전합니다. 다른 시스템에서는 그렇게하는 것이 안전 void *하거나 캐스팅이 아닌 포인터를 전달하는 것이 안전하지는 않습니다 .


3
나는 표준 프로모션과 variadic 논쟁에 대해 약간 혼란스러워합니다. 모든 포인터가 표준으로 홍보 void*됩니까? 그렇지 않으면 int*2 바이트이고 void*4 바이트라면 인수에서 4 바이트를 읽는 것이 분명히 오류일까요?
Kerrek SB

POSIX (POSIX 2013)에 대한 업데이트는 2.12.3 절을 제거하여 대부분의 요구 사항을 dlsym()대신 함수로 옮겼습니다 . 언젠가 나는 변화를 쓸 것이다. 그러나 '하루'는 '오늘'이 아니다.
Jonathan Leffler 2016 년

이 답변은 함수에 대한 포인터에도 적용됩니까? 그들은로 변환 될 수 있습니까 void *? 흠 여기에 귀하의 의견을 참조 하십시오 . 1 와트 변환 만 필요하기 때문에 (함수 포인터 void *)가 작동합니까?
chux-복원 Monica Monica

@ chux : 엄격하게 대답은 '아니오'이지만 실제로 대답은 '예'입니다. C 표준은 void *정보 손실없이 함수 포인터를 앞뒤로 변환 할 수 있음을 보증하지 않습니다 . 실제로 함수 포인터의 크기가 객체 포인터의 크기와 동일하지 않은 기계는 거의 없습니다. 나는 표준이 변환에 문제가있는 기계에서 함수 포인터를 인쇄하는 방법을 제공한다고 생각하지 않습니다.
Jonathan Leffler

"정보 손실없이 되돌아 가기"는 인쇄와 관련이 없습니다. 도움이 되나요?
chux-복원 Monica Monica

50

p포인터를 인쇄하는 변환 지정자입니다. 이것을 사용하십시오.

int a = 42;

printf("%p\n", (void *) &a);

캐스트 생략은 정의되지 않은 동작이며 p변환 지정자를 사용한 인쇄 는 구현 정의 방식으로 수행됩니다.


2
용서, 왜 캐스트를 생략하는 것이 "정의되지 않은 행동"입니까? 필요한 변수가 주소가 아닌 경우 변수의 주소가 중요합니까?
valdo

9
C가 말하기 때문에 @valdo (C99, 7.19.6.1p8) "p 인수는 void에 대한 포인터 여야합니다."
ouah

12
@valdo : 모든 포인터가 같은 크기 / 표현 인 것은 아닙니다.
caf

32

%p"포인터"에는를 사용 하고 다른 것은 사용하지 마십시오 *. 표준에 따라 포인터를 특정 유형의 정수처럼 취급 할 수 있다고 보장하지 않으므로 실제로 정수 형식으로 정의되지 않은 동작을 얻을 수 있습니다. (예를 들어,을 %u예상 unsigned int하지만 ? void*와 크기 또는 정렬 요구 사항이 다른 경우 unsigned int어떻게됩니까?)

*)를 참조 조나단의 좋은 답변! 대안으로 %p, 당신은 할 수 에서 포인터를 특정 매크로 사용 <inttypes.h>C99에서 추가를.

모든 객체 포인터는 void*C에서 암시 적으로 변환 가능 하지만 포인터를 가변 인수로 전달하려면 명시 적으로 캐스팅해야합니다 (임의의 객체 포인터는 변환 가능 하지만 void 포인터와 동일 하지 않기 때문에 ).

printf("x lives at %p.\n", (void*)&x);

2
모든 객체 포인터는 void *( printf()변형 함수이기 때문에 기술적으로 명시 적 캐스트가 필요 하지만) 변환 가능합니다 . 함수 포인터는 반드시로 변환 할 필요는 없습니다 void *.
caf

@ caf : 아, 나는 variadic 인수에 대해 몰랐어요-수정! 감사!
Kerrek SB

2
표준 C는 void *함수 포인터를 손실없이 함수 포인터로 변환 하거나 다시 변환 할 것을 요구하지 않습니다 . 그러나 POSIX는 표준 C의 일부가 아니라는 점을 명시 적으로 요구합니다. 따라서 실제로는 그것을 멀리 갈 수 있지만 (로 변환 하고 다시 변환 void (*function)(void)할 수는 있지만) C 표준에 의해 엄격하게 요구되지는 않습니다. void *void (*function)(void)
Jonathan Leffler

2
Jonathan and R .: 이것은 모두 매우 흥미 롭습니다. 그러나 여기서 함수 포인터를 인쇄하지 않을 것이라고 확신합니다. 따라서 아마도 이것에 대해 논의하기에 적절한 곳이 아닐 수도 있습니다. 나는 내 주장이 사용하지 않기 위해 여기에 약간의 지원을보고 싶다 %u!
Kerrek SB 2016

2
%u%lu에 잘못 모든 기계 가 아닌 일부 기계. printf전달 된 형식이 형식 지정자에 필요한 형식과 일치하지 않으면 동작이 정의되지 않는다는 사양 이 매우 명확합니다. 유형의 크기가 일치하는지 여부 (기계에 따라 true 또는 false 일 수 있음)는 관련이 없습니다. 일치해야하는 유형이며 절대로 그렇지 않습니다.
R .. GitHub 중지 지원 얼음

9

다른 (좋은) 대답하는 대신에 캐스트 할 수 uintptr_t또는 intptr_t(에서 stdint.h/ inttypes.h)와 해당 정수 변환 지정자를 사용합니다. 이렇게하면 포인터의 형식을 지정하는 방법이 더 유연 해지지 만 이러한 typedef를 제공하기 위해 구현을 엄격하게 말할 필요는 없습니다.


고려 #include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); } 가 사용하는 변수의 주소를 인쇄하는 정의되지 않은 동작입니다 %u형식 지정자는? 대부분의 경우 변수 주소는 양수이므로 %u대신 사용할 수 %p있습니까?
Destructor

1
@Destructor : 아니요 %u. 형식의 형식이며 unsigned int에 대한 포인터 인수와 함께 사용할 수 없습니다 printf.
R .. GitHub STOP ICE HELPING ICE

-1

당신은 사용할 수 있습니다 %x또는 %X또는 %p; 모두 정확합니다.

  • 을 사용하는 경우 %x주소는 소문자로 제공됩니다 (예 :a3bfbc4
  • 를 사용하는 경우 %X주소는 대문자로 제공됩니다 (예 :A3BFBC4

둘 다 맞습니다.

주소 를 사용 %x하거나 %X주소를 6 자리로 고려하고 있다면 주소를 %p8 자리로 고려하고 있습니다. 예를 들면 다음과 같습니다.


1
SO에 오신 것을 환영합니다. 다른 답변을 검토하기 위해 시간을 내십시오. 그들은 당신이 간과하고있는 많은 세부 사항들을 명확하게 설명하고 있습니다.
AntoineL
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.