이 프로그램은 어떻게 작동합니까?


88
#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", a);
    return 0;
}

그것은 표시 0! 어떻게 가능합니까? 이유는 무엇입니까?


나는 일부러 넣어 가지고 %d에서 printf의 동작을 연구하기 위해 문 printf.

답변:


239

%d는 기대 int하지만 플로트를 제공 했기 때문 입니다.

%e/ %f/ %g를 사용 하여 플로트를 인쇄합니다.


0이 인쇄되는 이유 : 부동 소수점 숫자가로 double전송되기 전에 로 변환 됩니다 printf. 리틀 엔디안의 이중 표현에서 숫자 1234.5는 다음과 같습니다.

00 00 00 00  00 4A 93 40

A %d는 32 비트 정수를 사용하므로 0이 인쇄됩니다. (테스트 printf("%d, %d\n", 1234.5f);로 출력을 얻을 수 있습니다 0, 1083394560.)


printf의 프로토 타입이 6.5.2.2/7에서 로 float변환 된 이유는doubleint printf(const char*, ...)

함수 프로토 타입 선언자의 줄임표 표기법으로 인해 마지막으로 선언 된 매개 변수 이후에 인수 유형 변환이 중지됩니다. 기본 인수 승격은 후행 인수에서 수행됩니다.

그리고 6.5.2.2/6부터,

호출 된 함수를 나타내는 표현식에 프로토 타입이 포함되지 않은 유형이있는 경우 정수 승격은 각 인수에 대해 수행되고 유형 float이 있는 인수 는로 승격됩니다 double. 이를 기본 인수 승격 이라고합니다 .

(이것을 찾아 주신 Alok에게 감사드립니다.)


4
+1 베스트 답변. "표준 기술적으로 올바른"이유와 "가능한 구현"이유에 모두 대답합니다.
Chris Lutz

12
그가 찾고 있던 답을 실제로 제공 한 12 명 중 당신은 유일한 사람이라고 생각합니다.
Gabe

11
때문에이 printf가변 인수 함수, 표준은 가변 기능, A가 있다고 float변환됩니다 double전달하기 전에.
Alok Singhal

8
C 표준에서 : "함수 프로토 타입 선언자의 생략 기호로 인해 마지막으로 선언 된 매개 변수 이후에 인수 유형 변환이 중지됩니다. 기본 인수 승격은 후행 인수에서 수행됩니다." 및 "... 및 float 유형의 인수는 double로 승격됩니다.이를 기본 인수 승격 이라고합니다 ."
Alok Singhal

2
에서 잘못된 형식 지정자를 사용 printf()하면 Undefined Behavior 가 호출 됩니다.
Prasoon Saurav

45

기술적으로 더이 말하기하지 , 각 라이브러리 구현 자체, 따라서, 연구 노력하는 당신의 방법 이 많이 사용 될 수 없습니다 무엇을하고 있는지 수행하여 행동의. 시스템에서 의 동작을 연구하려고 할 수 있습니다. 그렇다면 문서를 읽고 소스 코드를 살펴보아야합니다. printfprintfprintfprintf 라이브러리에서 사용할 수 있는지 합니다.

예를 들어, Macbook에서 1606416304프로그램과 함께 출력 을 얻습니다 .

즉, a float를 가변 함수에 float전달할 때는 double. 그래서, 당신의 프로그램이 선언 한에 해당 aA와 double.

의 바이트를 조사하려면 다음 답변을double 볼 수 있습니다. 여기에서 최근 질문에 대한 을 .

그걸하자:

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    unsigned char *p = (unsigned char *)&a;
    size_t i;

    printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
    for (i=0; i < sizeof a; ++i)
        printf("%02x ", p[i]);
    putchar('\n');
    return 0;
}

위의 프로그램을 실행하면 다음과 같은 결과가 나타납니다.

size of double: 8, int: 4
00 00 00 00 00 4a 93 40 

따라서의 처음 4 바이트는 double0으로 판명되었으며, 이것이 호출 0의 출력으로 얻은 이유 일 수 있습니다 printf.

더 흥미로운 결과를 위해 프로그램을 약간 변경할 수 있습니다.

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    int b = 42;

    printf("%d %d\n", a, b);
    return 0;
}

Macbook에서 위의 프로그램을 실행하면 다음과 같은 결과가 나타납니다.

42 1606416384

Linux 시스템에서 동일한 프로그램을 사용하면 다음을 얻습니다.

0 1083394560

왜 거꾸로 인쇄를 프로그래밍 했습니까? 내가 뭔가를 놓치고 있습니까? b가 int = 42이고 '% d'가 정수 형식 인 경우 printf 인수의 두 번째 변수이기 때문에 두 번째 인쇄 된 값이 아닌 이유는 무엇입니까? 오타입니까?
Katastic Voyage 2017

아마도 int인수가 인수와 다른 레지스터에서 전달 되기 때문일 것 double입니다. printfwith %dint42 라는 인수를 취하고 %d두 번째 int인수 는 두 번째 인수 가 없기 때문에 아마도 쓰레기를 인쇄합니다 .
Alok Singhal 2017 년

20

%d지정 알려줍니다 printf정수를 기대 할 수 있습니다. 따라서 float의 처음 4 개 (또는 플랫폼에 따라 2 개) 바이트는 정수로 해석됩니다. 0이면 0이 인쇄됩니다.

1234.5의 이진 표현은 다음과 같습니다.

1.00110100101 * 2^10 (exponent is decimal ...)

float실제로 IEEE754 이중 값으로 나타내는 C 컴파일러를 사용 하면 바이트가됩니다 (실수하지 않은 경우).

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000

endianess가 거의없는 Intel (x86) 시스템 (즉, 최하위 바이트가 먼저 오는)에서는이 바이트 시퀀스가 ​​반전되어 처음 4 바이트가 0이됩니다. 즉, printf인쇄되는 것은 ...

IEEE754에 따른 부동 소수점 표현 은 이 Wikipedia 기사 를 참조하십시오 .


7

이진법의 부동 소수점 표현 때문입니다. 정수로 변환하면 0이 남습니다.


1
당신은 그가 요구하는 것을 이해 한 유일한 사람인 것 같습니다. 물론 나 자신을 착각하지 않는 한.
Mizipzor 2010 년

답변이 부정확하고 불완전하다는 데 동의하지만 틀린 것은 아닙니다.
Shaihi 2010 년

7

정의되지 않은 동작을 호출했기 때문에 : 매개 변수 유형에 대해 거짓말을하여 printf () 메서드의 계약을 위반 했으므로 컴파일러는 원하는대로 자유롭게 수행 할 수 있습니다. 프로그램 출력을 "dksjalk is a ninnyhead !!!"로 만들 수 있습니다. 기술적으로는 여전히 옳습니다.


5

그 이유는 printf()꽤 멍청한 기능이기 때문입니다 . 유형을 전혀 확인하지 않습니다. 첫 번째 인수가라고 말하면 int(그리고 이것이 당신이 말하는 것입니다 %d), 그것은 당신을 믿고 int. 이 경우 컴퓨터가 4 바이트 int와 8 바이트 double( 내부 float로 변환 됨)를 사용한다고 가정하면 의 처음 4 바이트는 0이되고 인쇄됩니다.doubleprintf()a


3

자동으로 부동 소수점을 정수로 변환하지 않습니다. 둘 다 저장 형식이 다르기 때문입니다. 따라서 변환하려면 (int) typecasting을 사용하십시오.

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", (int)a);
    return 0;
}

2

C ++로 태그를 지정 했으므로이 코드 는 예상대로 변환을 수행합니다.

#include <iostream.h>

int main() {
    float a = 1234.5f;
    std::cout << a << " " << (int)a << "\n";
    return 0;
}

산출:

1234.5 1234


1

관련 데이터 유형 (int, float, string 등)과 함께 적절한 형식 지정자 (% d, % f, % s 등)를 사용하기 만하면됩니다.


문제는 그것을 고치는 방법이 아니라 그것이 작동하는 방식으로 작동하는 이유입니다.
ya23 2010 년


0

헤이 그것은 무언가를 인쇄해야했기 때문에 0을 인쇄했습니다. C 0은 다른 모든 것임을 기억하십시오!


1
어때요? C에는 다른 모든 것과 같은 것이 없습니다.
Vlad

x가 어떤 경우에이면, x == 0 :!
chunkyguy

if (iGot == "everything") print "everything"; 그렇지 않으면 "아무것도"인쇄하지 않습니다.
nik

0

정수가 아닙니다. 을 사용해보십시오 %f.


질문은 왜 float를 int로 변환하고 "1234"를 표시하지 않는 것일까 요?
Björn Pollex 2010 년

c가 논리적으로 말이되지 않는 일을하게하지 않기를 기대하지 마십시오. 예, 많은 언어가 예상 할 수있는 1234를 제공 할 것이며 아마도 c의 일부 구현조차도이 동작이 정의되었다고 생각하지 않을 것입니다. C를 사용하면 부모처럼 자신을 목 매어 죽일 수 있습니다.
다시 실행

C는 유연하게 설계 되었기 때문입니다.
tangrs 2010 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.