C에서 % n 형식 지정자의 사용은 무엇입니까?


125

%nC 에서 형식 지정자 의 사용은 무엇입니까 ? 누구든지 예를 들어 설명 할 수 있습니까?


25
훌륭한 매뉴얼을 읽는 순수 예술은 어떻게 되었습니까?
Jens

8
진짜 질문은 이런 옵션 의 POINT 가 무엇 일까요? 왜 누군가가 인쇄 된 문자 수의 값을 알고 싶어하는 이유는 그 값을 메모리에 직접 쓰는 것이 훨씬 적습니다. 그것은 지루했다 개발자 같았다과 커널 인에 버그를 도입하기로 결정
지아 첸

그것이 Bionic이 그것을 놓아 준 이유입니다.
solidak

1
사실 이것은 타당한 질문이며 훌륭한 매뉴얼이 대답하지 않을 가능성이 높습니다. 실수로 Turing-complete 를 %n만드는 것으로 밝혀졌으며 printf예를 들어 Brainfuck을 구현할 수 있습니다. github.com/HexHive/printbfoilshell.org/blog/2019/02/07.html#appendix-a-minor-sublanguages
John Frazer

답변:


147

아무것도 인쇄되지 않았습니다. 인수는 지금까지 기록 된 문자 수가 저장되는 signed int에 대한 포인터 여야합니다.

#include <stdio.h>

int main()
{
  int val;

  printf("blah %n blah\n", &val);

  printf("val = %d\n", val);

  return 0;

}

이전 코드는 다음을 인쇄합니다.

blah  blah
val = 5

1
인수가 signed int에 대한 포인터 여야한다고 언급 한 다음 예제에서 unsigned int를 사용했습니다 (아마도 오타 일 것입니다).
bta 2010 년

1
@AndrewS : 함수가 변수 값을 수정하기 때문입니다.
Jack

3
@Jack int은 항상 서명됩니다.
jamesdlin 2014-06-20

1
@jamesdlin : 내 실수. 미안 해요 .. 어디서 읽었는지 몰랐어요.
Jack

1
어떤 이유로 샘플은 note와 함께 오류를 발생시킵니다 n format specifies disabled. 이유가 뭐야?
Johnny_D

186

이 답변의 대부분 %n (아무것도 인쇄하지 않고 지금까지 인쇄 된 문자 수를 int변수 에 쓰는 것입니다) 무엇을하는지 설명 하지만 지금까지 아무도 그것이 무엇을 사용 하는지에 대한 예를 제공하지 않았습니다 . 다음은 하나입니다.

int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");

인쇄됩니다 :

hello: Foo
       Bar

Foo와 Bar가 정렬되어 있습니다. ( %n이 특정 예제 를 사용하지 않고 그렇게하는 것은 사소한 일이며 일반적으로 항상 첫 번째 printf호출을 끊을 수 있습니다 .

int n = printf("%s: ", "hello");
printf("Foo\n");
printf("%*sBar\n", n, "");

약간 추가 된 편리함이 난해한 것과 같은 것을 사용할 가치가 있는지 %n(그리고 오류를 유발할 수 있음) 여부는 논쟁의 여지가 있습니다.)


3
오 마이-이것은 주어진 글꼴에서 문자열의 픽셀 크기를 계산하는 문자 기반 버전입니다!

& n 및 * s가 필요한 이유를 설명해 주시겠습니까? 둘 다 포인터입니까?
Andrew S

9
@AndrewS &n는 포인터 ( &연산자의 주소)입니다. C는 값에 의한 전달이고 포인터 printf가 없으면의 값을 수정할 수 없기 때문에 포인터가 필요 n합니다. 형식 문자열 %*s에서 의 사용법 은 문자 의 필드 너비 를 사용하여 지정자 (이 경우 빈 문자열 )를 printf인쇄합니다 . 기본 원칙에 대한 설명 은 기본적으로이 질문 (및 답변)의 범위를 벗어납니다. 문서를 읽 거나 SO에 대한 별도의 질문을 하는 것이 좋습니다 . %s""nprintfprintf
jamesdlin

3
사용 사례를 보여 주셔서 감사합니다. 왜 사람들이 기본적으로 매뉴얼을 SO에 복사하여 붙여넣고 가끔 바꾸는 이유를 이해하지 못합니다. 우리는 인간이고 모든 것이 항상 대답으로 설명되어야 하는 이유 때문에 이루어집니다 . "아무것도하지 않는다"는 말은 거의 쓸모없는 지식인 "멋진 단어는 멋지다"라고 말하는 것과 같습니다.
the_endian 17.01.25

1
@PSkocik 간접적 인 수준을 추가하지 않고도 복잡하고 오류가 발생하기 쉽습니다.
jamesdlin 19

18

실제로 %n지정자 의 실제 사용을 많이 보지 못했지만 형식 문자열 공격 과 함께 구식 printf 취약성에서 꽤 오래 전에 사용되었다는 것을 기억합니다 .

이렇게 된 것

void authorizeUser( char * username, char * password){

    ...code here setting authorized to false...
    printf(username);

    if ( authorized ) {
         giveControl(username);
    }
}

악의적 인 사용자가 형식 문자열로 printf에 전달되는 사용자 이름 매개 변수를 이용하고 %d, %c또는 w / e 조합을 사용 하여 호출 스택을 통과 한 다음 실제 값으로 승인 된 변수를 수정할 수 있습니다.

예, 그것은 난해한 용도이지만 보안 허점을 피하기 위해 데몬을 작성할 때 항상 유용합니까? :디


1
%n확인되지 않은 입력 문자열을 printf형식 문자열 로 사용하지 않는 것보다 더 많은 이유가 있습니다.
Keith Thompson

13

여기 에서 지금까지 인쇄 된 문자 수를 저장하는 것을 볼 수 있습니다.

n 인수는 fprintf()함수 중 하나에 대한이 호출에 의해 지금까지 출력에 기록 된 바이트 수를 기록하는 정수에 대한 포인터 입니다. 인수는 변환되지 않습니다.

사용 예는 다음과 같습니다.

int n_chars = 0;
printf("Hello, World%n", &n_chars);

n_chars그러면 값이 12.


10

지금까지 모든 대답은 그에 대한 %n것이지만, 처음에 누군가가 그것을 원하는 이유는 아닙니다. 저장된 값이 결과 문자열에 대한 배열 인덱스이기 때문에 나중에 결과 문자열을 분리하거나 수정해야 할 때 sprintf/ snprintf를 사용하면 다소 유용 합니다. 이 응용 프로그램은를 사용하면 훨씬 더 유용합니다. sscanf특히 scanf패밀리의 함수는 처리 된 문자 수를 반환하지 않고 필드 수를 반환하기 때문입니다.

또 다른 정말 hackish 사용은 다른 작업의 일부로 숫자를 인쇄하는 동안 동시에 무료로 pseudo-log10을 얻는 것입니다.


에 대한 사용을 언급하는 데 +1. %n"모든 답변 ..."에 대해 다른 점을 간청합니다. = P
jamesdlin 2010 년

1
악당들은 printf / % n, sprintf, sscanf를 사용 해주셔서 감사합니다;)
jww

6
@noloader : 어떻게? 를 사용 %n 하면 공격자에게 취약 할 위험이 전혀 없습니다. 잘못 배치 된 악명은 %n형식 인수로 형식 문자열이 아닌 메시지 문자열을 전달하는 어리석은 관행에 속합니다. 물론이 상황 %n은 실제로 사용되는 의도적 인 형식 문자열의 일부인 경우 발생하지 않습니다 .
R .. GitHub의 STOP 돕기 ICE

% n을 사용하면 메모리에 쓸 수 있습니다. 나는 공격자가 그 포인터를 제어하지 않는다고 가정하고 있다고 생각합니다 (내가 틀릴 수 있습니다). 공격자가 포인터를 제어하는 ​​경우 (printf에 대한 또 다른 매개 변수 일뿐) 4 바이트 쓰기를 수행 할 수 있습니다. 그 / 그녀가 이익을 얻을 수 있는지 여부는 다른 이야기입니다.
jww

8
@noloader : 그것은 포인터 사용에 대해 사실입니다. 아무도 글을 쓴 것에 대해 "나쁜 놈들 고마워"라고 말하지 않습니다 *p = f();. %n포인터가 가리키는 객체에 결과를 쓰는 또 다른 방법 인을 포인터 자체가 위험하다고 생각하지 않고 "위험"으로 간주 해야하는 이유는 무엇 입니까?
R .. GitHub의 STOP 돕기 ICE

10

와 연관된 인수 %n는로 처리되며 int*.NET Framework에서 해당 지점에 인쇄 된 총 문자 수로 채워집니다 printf.


9

다른 날 나는 %n내 문제를 멋지게 해결할 수 있는 상황에 처했다. 이전 답변 과 달리이 경우 좋은 대안을 고안 할 수 없습니다.

지정된 텍스트를 표시하는 GUI 컨트롤이 있습니다. 이 컨트롤은 해당 텍스트의 일부를 굵게 (또는 기울임 꼴 또는 밑줄 등) 표시 할 수 있으며 시작 및 끝 문자 색인을 지정하여 어느 부분을 지정할 수 있습니다.

제 경우에는를 사용하여 컨트롤에 대한 텍스트를 생성하고 snprintf있으며 대체 항목 중 하나를 굵게 표시하고 싶습니다. 이 대체에 대한 시작 및 끝 인덱스를 찾는 것은 다음과 같은 이유로 중요하지 않습니다.

  • 문자열에는 여러 개의 대체가 포함되며 대체 중 하나는 임의의 사용자 지정 텍스트입니다. 이것은 내가 관심을 갖는 대체물에 대한 텍스트 검색을 수행하는 것이 잠재적으로 모호하다는 것을 의미합니다.

  • 형식 문자열은 지역화 될 수 있으며 $위치 형식 지정자에 POSIX 확장을 사용할 수 있습니다 . 따라서 원래 형식 문자열에서 형식 지정자 자체를 검색하는 것은 간단하지 않습니다.

  • 지역화 측면은 또한 형식 문자열을 snprintf.

따라서 특정 대체에 대한 인덱스를 찾는 가장 간단한 방법은 다음과 같습니다.

char buf[256];
int start;
int end;

snprintf(buf, sizeof buf,
         "blah blah %s %f yada yada %n%s%n yakety yak",
         someUserSpecifiedString,
         someFloat,
         &start, boldString, &end);
control->set_text(buf);
control->set_bold(start, end);

유스 케이스에 +1을 드리겠습니다. 그러나 감사에 실패 할 것이므로 굵은 텍스트의 시작과 끝을 표시하는 다른 방법을 고안해야합니다. 작성된 문자 수를 반환 snprintf하므로 snprintf반환 값을 확인하는 동안 세 가지 가 잘 작동하는 것 같습니다 . 어쩌면 다음과 같을 수도 있습니다 : int begin = snprintf(..., "blah blah %s %f yada yada", ...);그리고 int end = snprintf(..., "%s", ...);꼬리 : snprintf(..., "blah blah");.
jww

3
@jww 여러 snprintf호출 의 문제점 은 대체가 다른 로케일에서 재 배열 될 수 있으므로 그렇게 나눌 수 없다는 것입니다.
jamesdlin

예를 들어 주셔서 감사합니다. 그러나 필드 바로 앞에 출력을 굵게 표시 한 다음 그 뒤에 시퀀스를 작성하는 터미널 제어 시퀀스를 작성할 수 없습니까? 터미널 제어 시퀀스를 하드 코딩하지 않으면 위치 (재정렬 가능)로 만들 수도 있습니다.
PSkocik

1
@PSkocik 터미널로 출력하는 경우 . 예를 들어 Win32 리치 편집 컨트롤로 작업하는 경우 나중에 돌아가서 터미널 컨트롤 시퀀스를 구문 분석하지 않는 한 도움이되지 않습니다. 또한 대체 된 텍스트의 나머지 부분에서 터미널 제어 시퀀스를 준수한다고 가정합니다. 그렇지 않은 경우 필터링하거나 이스케이프해야합니다. 나는 그것이 없이는 불가능하다고 말하는 것이 아니다 %n. 나는 사용 %n이 대안보다 더 간단 하다고 주장하고 있습니다.
jamesdlin 19

1

아무것도 인쇄하지 않습니다. %n형식 문자열에 나타나기 전에 인쇄 된 문자 수를 파악 하고 제공된 int에 출력하는 데 사용됩니다.

#include <stdio.h>

int main(int argc, char* argv[])
{
    int resultOfNSpecifier = 0;
    _set_printf_count_output(1); /* Required in visual studio */
    printf("Some format string%n\n", &resultOfNSpecifier);
    printf("Count of chars before the %%n: %d\n", resultOfNSpecifier);
    return 0;
}

(에 대한 문서_set_printf_count_output )


0

해당 printf()기능에 지금까지 인쇄 된 문자 수 값을 저장 합니다.

예:

int a;
printf("Hello World %n \n", &a);
printf("Characters printed so far = %d",a);

이 프로그램의 출력은 다음과 같습니다.

Hello World
Characters printed so far = 12

코드를 입력하면 다음과 같은 결과가 나타납니다. 지금까지 인쇄 된 Hello World 문자 = 36 ,,,, 왜 36 ?! Windows 컴퓨터에서 32 비트 GCC를 사용합니다.
Sina Karvandi

-6

% n은 C99이며 VC ++에서는 작동하지 않습니다.


2
%nC89에 존재했습니다. Microsoft는 보안 문제를 위해 기본적으로 비활성화했기 때문에 MSVC에서는 작동하지 않습니다. _set_printf_count_output활성화 하려면 먼저 호출해야 합니다. (Merlyn Morgan-Graham의 답변 참조)
jamesdlin 2010-08-05

아니요, C89는이 기능 / 백도어 버그를 정의하지 않습니다. K & R + ANSI-C 참조 amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/… ?? 댓글에 대한 URL 태그는 어디에 있습니까 ??
user411313 2010-08-05

4
당신은 단순히 틀 렸습니다. printfK & R 2 판 부록 B의 표 B-1 ( 변환)에 명시되어 있습니다 . (내 사본의 244 페이지) 또는 ISO C90 사양의 섹션 7.9.6.1 (134 페이지)을 참조하십시오.
jamesdlin

Android는 또한 % n 지정자를 제거했습니다.
jww

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