C ++에서 'printf'와 'cout'


답변:


332

이 질문의 모든 사람들이 질문이 단지 차이점을 요구하더라도. std::cout보다 더 낫다고 주장하는 것에 놀랐습니다 printf. 이제 차이점 std::cout은 C ++ printf이며 C입니다 (그러나 C의 거의 다른 것과 마찬가지로 C ++에서 사용할 수 있습니다 ). 이제 솔직히 말하겠습니다. 모두 printfstd::cout그들의 장점이있다.

실제 차이점

확장 성

std::cout확장 가능합니다. 사람들 printf은 확장 할 수 있다고 말하지만 그러한 확장은 C 표준에 언급되어 있지 않으므로 비표준 기능을 사용해야하지만 일반적인 비표준 기능은 존재하지 않습니다. (이미 존재하는 형식과 충돌하기 쉽습니다).

달리 printf, std::cout연산자 오버로딩에 완전히 의존하므로 사용자 정의 형식과 아무 문제 없다 - 당신이 할 모든 서브 루틴 복용 정의입니다 std::ostream첫 번째 인수로하고 두 번째와 같은 유형입니다. 따라서 네임 스페이스 문제는 없습니다. 클래스가 한 문자로 제한되지 않는 한 std::ostream오버로드 작업을 수행 할 수 있습니다 .

그러나 많은 사람들이 확장하기를 원한다고 의심합니다 ostream(솔직히 말하면 쉽게 확장 할 수는 없지만 그러한 확장을 거의 보지 못했습니다). 그러나 필요한 경우 여기에 있습니다.

통사론

그것은 쉽게 발견 할 수 있기 때문에, 모두 printf와는 std::cout다른 구문을 사용합니다. printf패턴 문자열 및 가변 길이 인수 목록을 사용하여 표준 함수 구문을 사용합니다. 사실, printfC가 가지고있는 이유입니다- printf형식이 너무 복잡하여 사용할 수 없습니다. 그러나 std::cout다른 API ( operator <<자체를 반환 하는 API)를 사용합니다.

일반적으로 C 버전이 더 짧아 지지만 대부분의 경우 중요하지 않습니다. 많은 인수를 인쇄하면 차이점이 눈에 is니다. Error 2: File not found.오류 번호를 가정 하고와 같은 것을 작성해야 하고 설명이 자리 표시 자 인 경우 코드는 다음과 같습니다. 두 예제는 모두 동일하게 작동합니다 ( std::endl실제로 버퍼를 비 웁니다).

printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;

이것이 너무 미친 것처럼 보이지는 않지만 (두 배 길어집니다) 실제로 인수를 인쇄하는 대신 인수를 형식화하면 상황이 더 미쳐집니다. 예를 들어, 같은 것을 인쇄하는 것은 0x0424미친 짓입니다. 이것은 std::cout혼합 상태와 실제 값으로 인해 발생 합니다. 나는 std::setfill타입이 같은 언어를 보지 못했습니다 (물론 C ++ 이외). printf인수와 실제 유형을 명확하게 구분합니다. 나는 잡음이 너무 많기 때문에 버전 printf과 비교하여 iostream버전 을 유지하는 것을 선호합니다 .

printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;

번역

이것이 printf거짓말 의 진정한 이점입니다 . printf형식 문자열은 ... 음 문자열입니다. 그것은의 operator <<남용에 비해 번역하기가 정말 쉽습니다 iostream. 가정하면 gettext()함수가 변환하고 표시 할 Error 2: File not found.같을 것이다 이전에 표시된 형식 문자열의 번역을 얻기 위해 코드를 :

printf(gettext("Error %d: %s.\n"), id, errors[id]);

이제 오류 번호가 설명 뒤에 나오는 Fictionish로 변환한다고 가정하겠습니다. 번역 된 문자열은 다음과 같습니다 %2$s oru %1$d.\n. 이제 C ++에서 어떻게해야합니까? 글쎄요 나는 당신 이 번역의 목적으로 전달할 수있는 가짜 iostream구문 printf을 만들 수 있다고 생각합니다 gettext. 물론, $C 표준은 아니지만 내 의견으로는 사용하기에 너무 일반적입니다.

특정 정수 유형 구문을 기억 / 조회 할 필요가 없습니다.

C에는 많은 정수 유형이 있으며 C ++도 마찬가지입니다. std::cout당신을 위해 핸들 모든 유형 동안 printf정수 유형에 따라 특정 구문을 필요는 (정수가 아닌 종류가 있습니다,하지만 당신이 실제로 사용할 수있는 유일한 정수가 아닌 유형 printf입니다 const char *(C 문자열을 사용하여 얻을 수있는 to_c방법을 std::string)). 예를 들어, 인쇄 size_t하려면을 사용해야 %zd하지만 int64_t을 사용해야합니다 %"PRId64". 표는 http://en.cppreference.com/w/cpp/io/c/fprintfhttp://en.cppreference.com/w/cpp/types/integer 에서 사용 가능 합니다.

NUL 바이트를 인쇄 할 수 없습니다. \0

때문에 printfC ++ 문자열이 아닌 용도의 C 문자열이 특정 트릭없이 NUL 바이트를 인쇄 할 수 없습니다. 어떤 경우에는 그것을 사용하는 것이 가능 %c하여 '\0'그 분명히 해킹 있지만 인수로.

아무도 신경 쓰지 않는 차이점

공연

업데이트 : iostream속도가 너무 느려 일반적으로 하드 드라이브보다 느립니다 (프로그램을 파일로 리디렉션하는 경우). stdio많은 양의 데이터를 출력해야하는 경우 동기화를 비활성화 하면 도움 이 될 수 있습니다. STDOUT에 여러 줄을 쓰는 것과는 대조적으로 성능이 실제로 문제가되는 경우을 사용하십시오 printf.

누구나 성능에 관심이 있다고 생각하지만 아무도 그것을 측정 할 필요가 없습니다. 내 대답은 당신이 사용하는 경우 I / O는, 어쨌든 병목 현상없이하다는 것이다 printfiostream. 어셈블리를 빠르게 살펴보면 컴파일러 옵션 이 더 빠를 printf 있다고 생각합니다 ( -O3컴파일러 옵션을 사용하여 clang으로 컴파일 ). 내 오류 예제를 가정하면 printf예제는 cout예제 보다 적은 호출을 수행합니다 . 이다 int main와 함께 printf:

main:                                   @ @main
@ BB#0:
        push    {lr}
        ldr     r0, .LCPI0_0
        ldr     r2, .LCPI0_1
        mov     r1, #2
        bl      printf
        mov     r0, #0
        pop     {lr}
        mov     pc, lr
        .align  2
@ BB#1:

두 개의 문자열과 2(숫자)가 printf인수 로 푸시되는 것을 쉽게 알 수 있습니다 . 그게 다야. 다른 것은 없습니다. 비교 iostream를 위해 어셈블리 로 컴파일됩니다. 아니요, 인라이닝이 없습니다. 모든 단일 operator <<호출은 다른 인수 세트를 갖는 다른 호출을 의미합니다.

main:                                   @ @main
@ BB#0:
        push    {r4, r5, lr}
        ldr     r4, .LCPI0_0
        ldr     r1, .LCPI0_1
        mov     r2, #6
        mov     r3, #0
        mov     r0, r4
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        mov     r0, r4
        mov     r1, #2
        bl      _ZNSolsEi
        ldr     r1, .LCPI0_2
        mov     r2, #2
        mov     r3, #0
        mov     r4, r0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r1, .LCPI0_3
        mov     r0, r4
        mov     r2, #14
        mov     r3, #0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r1, .LCPI0_4
        mov     r0, r4
        mov     r2, #1
        mov     r3, #0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r0, [r4]
        sub     r0, r0, #24
        ldr     r0, [r0]
        add     r0, r0, r4
        ldr     r5, [r0, #240]
        cmp     r5, #0
        beq     .LBB0_5
@ BB#1:                                 @ %_ZSt13__check_facetISt5ctypeIcEERKT_PS3_.exit
        ldrb    r0, [r5, #28]
        cmp     r0, #0
        beq     .LBB0_3
@ BB#2:
        ldrb    r0, [r5, #39]
        b       .LBB0_4
.LBB0_3:
        mov     r0, r5
        bl      _ZNKSt5ctypeIcE13_M_widen_initEv
        ldr     r0, [r5]
        mov     r1, #10
        ldr     r2, [r0, #24]
        mov     r0, r5
        mov     lr, pc
        mov     pc, r2
.LBB0_4:                                @ %_ZNKSt5ctypeIcE5widenEc.exit
        lsl     r0, r0, #24
        asr     r1, r0, #24
        mov     r0, r4
        bl      _ZNSo3putEc
        bl      _ZNSo5flushEv
        mov     r0, #0
        pop     {r4, r5, lr}
        mov     pc, lr
.LBB0_5:
        bl      _ZSt16__throw_bad_castv
        .align  2
@ BB#6:

그러나 솔직히 말하면 이것은 I / O가 병목 현상이기 때문에 아무 의미가 없습니다. 나는 iostream그것이 "유형 안전"이기 때문에 더 빠르지 않다는 것을 보여주고 싶었습니다 . 대부분의 C 구현은 printf계산 된 goto를 사용하여 형식을 구현 하므로 printf컴파일러가 인식하지 않아도 가능한 한 빠릅니다 printf(일부 컴파일러는 printf특정 경우에 최적화 할 수 없음 -상수 문자열로 끝나는 상수 \n는 일반적으로에 최적화 됨 puts) .

계승

왜 상속을 원하는지 모르겠지만 ostream상관하지 않습니다. 그것도 가능합니다 FILE.

class MyFile : public FILE {}

타입 안전

사실 가변 길이 인수 목록은 안전하지 않지만 printf경고를 활성화하면 널리 사용되는 C 컴파일러가 형식 문자열의 문제를 감지 할 수 있으므로 중요하지 않습니다 . 실제로 Clang은 경고를 활성화하지 않고도이를 수행 할 수 있습니다.

$ cat safety.c

#include <stdio.h>

int main(void) {
    printf("String: %s\n", 42);
    return 0;
}

$ clang safety.c

safety.c:4:28: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
    printf("String: %s\n", 42);
                    ~~     ^~
                    %d
1 warning generated.
$ gcc -Wall safety.c
safety.c: In function main’:
safety.c:4:5: warning: format ‘%s expects argument of type char *’, but argument 2 has type int [-Wformat=]
     printf("String: %s\n", 42);
     ^

18
어쨌든 I / O가 병목 현상이라고 말합니다. 분명히 당신은 그 가정을 테스트 하지 않았습니다 . "다른 한편으로, 75.3 MB / s의 iostreams 버전은 하드 디스크를 유지할 수있을 정도로 데이터를 빠르게 버퍼링 할 수 없습니다. 나쁘고 아직 실제 작업을 수행하고 있지 않습니다. "I / O 라이브러리가 디스크 컨트롤러를 포화시킬 수 있어야한다고 기대할 때 너무 높은 기대치가 아니라고 생각합니다."
Ben Voigt

4
@ BenVoigt : 가능하면 C ++을 피하려고 노력합니다. 나는 그것을 많이 사용해 보았지만, 내가 사용했던 다른 프로그래밍 언어보다 더 성 가시고 유지 보수가 쉽지 않았다. 이것은 ++ 나 C를 방지하기위한 또 다른 이유이다 - 전체 C를 ++ 라이브러리가 아마 제외하고, 대부분의 구현에서 느린 -이 (그것도 iostream 아니에요도 빠르지 std::sort에 비해 다소 놀라 울 정도로 빠른 인 qsort에서 2 회 () 실행 파일 크기 비용).
Konrad Borowski

3
cout을 사용할 때 병렬 환경에서 문제를 언급 한 사람은 없습니다.
Nicholas Hamilton

9
당신의 퍼포먼스 논쟁은 아무 의미가 없습니다. 프로그램에서 더 많은 어셈블리가 있다고해서 프로그램 속도가 느려지는 것은 아닙니다. 왜냐하면 printf 기능을 만드는 모든 코드 (많은 코드)를 설명 하지 않기 때문 입니다. 제 생각에는 컴파일러가 변수와 형식을 더 잘 이해할 수 있기 때문에 printf보다 << 연산자로 cout을 최적화하는 것이 가능합니다.
Ignas2526

18
나는이 답변에 대해 많은 것을 좋아하지만 아마도 내가 가장 좋아하는 부분은 "모두가 성능에 관심이 있다고 생각하지만 아무도 그것을 측정하는 것을 귀찮게하지는 않을 것입니다."
Kyle Strand

203

로부터 C ++ FAQ :

[15.1] 왜 <iostream> 전통적인 방식 대신에 사용해야 <cstdio>합니까?

형식 안전성을 높이고 오류를 줄이며 확장 성을 허용하며 상속성을 제공합니다.

printf()scanf()아마도 오류가 발생 하지 않았 음에도 불구하고 아마도 깨지지 않을 것입니다. 그러나 C ++ I / O가 할 수있는 것에 대해서는 둘 다 제한되어 있습니다. C ++ I / O ( <<및 사용 >>)는 C ( printf()및 사용 scanf())에 상대적입니다 .

  • 더 안전한 유형 :를 사용하면 <iostream>I / O되는 객체의 유형을 컴파일러에서 정적으로 알 수 있습니다. 반대로 <cstdio>"%"필드를 사용하여 유형을 동적으로 파악하십시오.
  • 오류 발생 가능성 감소 :을 사용하면 <iostream>I / O중인 실제 개체와 일치해야하는 중복 "%"토큰이 없습니다. 중복성을 제거하면 일련의 오류가 제거됩니다.
  • 확장 성 : C ++ <iostream>메커니즘을 사용하면 기존 코드를 손상시키지 않고 새로운 사용자 정의 유형을 I / O 할 수 있습니다. 모든 사람이 동시에 새로운 호환되지 않는 "%"필드를 추가 할 경우 혼란을 상상해 printf()scanf()?!
  • 상속 가능 : C ++ <iostream>메커니즘은 std::ostreamand와 같은 실제 클래스에서 빌드됩니다 std::istream. 달리 <cstdio>의 ' FILE*이 실제 수업 따라서 상속합니다. 즉, 스트림처럼 보이고 작동하는 다른 사용자 정의 항목을 가질 수 있지만 원하는 이상하고 멋진 작업을 수행 할 수 있습니다. 모르는 사용자가 작성한 수백만 줄의 I / O 코드를 자동으로 사용할 수 있으며 "확장 스트림"클래스에 대해 알 필요가 없습니다.

반면에, printf에 우선을 사용하여 정당화 할 수있는, 상당히 빠른 cout에서 매우 제한적인 특정 경우. 항상 먼저 프로파일하십시오. (예를 들어 http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout / 참조)


2
반면에 FastFormat 라이브러리 ( fastformat.org )는 형식 안전성, 표현성 및 성능을 한 번에 제공합니다. (아직 시도하지는 않았지만 ...)
xtofl

3
@Marcelo는 아마도 모든 것이 인용 된 좋은 요약이기 때문일 것입니다. 서식 ... 예, 꽤 나쁩니다. 나는 그 자신을 고쳐야했지만, 다른 사람들 (자신 포함)이 그것을 돌 보았을 것입니다. 물론, 그냥 징징 거리는 것보다 더 건설적인 것입니다.
Mikeage

2
늦게 printf()도 확장 가능해야합니다. udrepper.livejournal.com/20948.html의
Maxim Egorushkin

4
@MaximYegorushkin : 표준 printf에는 그런 능력이 없습니다. 이식 불가능한 라이브러리 메커니즘은 완전히 표준화 된 iostream 확장 성과 같은 수준에 있지 않습니다.
Ben Voigt

4
"반면에, printf는 상당히 빠르다"printf는 또한 더 깨끗하고 사용하기 쉽기 때문에 가능하면 cout을 피해야합니다.
FluorescentGreen5 5

43

사람들은 종종 그것이 printf훨씬 빠르다고 주장합니다 . 이것은 대부분 신화입니다. 방금 테스트 한 결과는 다음과 같습니다.

cout with only endl                     1461.310252 ms
cout with only '\n'                      343.080217 ms
printf with only '\n'                     90.295948 ms
cout with string constant and endl      1892.975381 ms
cout with string constant and '\n'       416.123446 ms
printf with string constant and '\n'     472.073070 ms
cout with some stuff and endl           3496.489748 ms
cout with some stuff and '\n'           2638.272046 ms
printf with some stuff and '\n'         2520.318314 ms

결론 : 줄 바꿈 만 원하면 printf; 그렇지 않으면 cout거의 빠르거나 훨씬 빠릅니다. 자세한 내용은 내 블로그 에서 확인할 수 있습니다 .

분명히하기 위해 iostreams가 항상보다 낫다고 말하려고하지는 않습니다 printf. 나는 단지 일반적인 데이터를 바탕으로 정보에 근거한 결정을 내려야한다고 말하려고합니다.

업데이트 : 테스트에 사용한 전체 코드는 다음과 같습니다. g++추가 옵션없이 컴파일 -lrt합니다 (타이밍 제외).

#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    timespec d_start;
    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            clock_gettime(CLOCK_REALTIME, &d_start);
        }
        ~TimedSection() {
            timespec end;
            clock_gettime(CLOCK_REALTIME, &end);
            double duration = 1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
            std::cerr << d_name << '\t' << std::fixed << duration << " ms\n"; 
        }
};

int main() {
    const int iters = 10000000;
    char const *text = "01234567890123456789";
    {
        TimedSection s("cout with only endl");
        for (int i = 0; i < iters; ++i)
            std::cout << std::endl;
    }
    {
        TimedSection s("cout with only '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << '\n';
    }
    {
        TimedSection s("printf with only '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("\n");
    }
    {
        TimedSection s("cout with string constant and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789" << std::endl;
    }
    {
        TimedSection s("cout with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789\n";
    }
    {
        TimedSection s("printf with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("01234567890123456789\n");
    }
    {
        TimedSection s("cout with some stuff and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << std::endl;
    }
    {
        TimedSection s("cout with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << '\n';
    }
    {
        TimedSection s("printf with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("%s01234567890123456789%i\n", text, i);
    }
}

5
당신의 점수에서 printf는 cout을 쉽게 친다 (대부분의 경우). 퍼프와 관련하여 왜 cout을 사용하는 것이 좋을지 궁금합니다. 비록 perf가 현실적인 경우에 그렇게 다르지 않다는 것에 동의하지만 ..
mishal153

3
@ mishal153 : 나는 성능이 너무 다르지 않다고 말하고 싶기 때문에 일반적으로 들리는 "왜냐하면 cout을 느리게 사용하지 마십시오"라는 평범한 조언은 어리 석습니다. cout은 형식 안전성이라는 명백한 장점이 있으며 가독성도 있습니다. (iostream을 사용한 부동 소수점 형식은 끔찍합니다 ...)
Thomas

35
사이의 중요한 차이점 printf()std::ostream하다 전 출력 단일 호출 모든 인수 반면 std::ostream초래 각각에 대해 개별적으로 호출 <<. 테스트는 하나의 인수와 줄 바꾸기 만 출력하므로 차이를 볼 수 없습니다.
Maxim Egorushkin

12
컴파일러는 이러한 호출을 인라인 할 수 있어야합니다. 또한 printf다양한 형식 지정자를 위해 도우미 함수를 다루기 위해 표지 아래에서 많은 호출을 할 수 있습니다. 그리고 다시 인라인으로 인해 속도에 전혀 차이가 없어야합니다.
토마스

4
터미널 시간을 정했습니다. 사용 sprintf또는 fprintfstringstreamfstream.
Ben Voigt

41

그리고 나는 인용한다 :

높은 수준의 용어의 주요 차이점은 형식 안전성 (cstdio에없는 것), 성능 (대부분의 iostream 구현이 cstdio보다 느리다) 및 확장 성 (iostream은 사용자 정의 출력 대상 및 사용자 정의 유형의 원활한 출력 허용)입니다.


특히 POSIX를 사용하는 유닉스에서는 typedef 중 하나의 크기가 실제로 무엇인지 알 수 없으므로 많은 캐스트가 필요하거나 99 %의 프로그램으로 % d로 위험에 처할 수 있습니다. % z가 C99와 함께 제공되기까지 오랜 시간이 걸렸습니다. 그러나 time_t / off_t의 경우 올바른 형식 명령에 대한 탐색이 계속됩니다.
Lothar

30

하나는 stdout으로 인쇄하는 기능입니다. 다른 하나는 여러 멤버 함수와 operator<<해당 인쇄의 과부하 를 stdout에 제공하는 객체입니다 . 열거 할 수있는 더 많은 차이점이 있지만, 당신이 무엇을하는지 잘 모르겠습니다.


12

나를 위해, 'printf'가 아닌 'cout'으로 갈 수있는 진정한 차이점은 다음과 같습니다.

1) << 연산자가 내 클래스에 과부하 될 수 있습니다.

2) cout의 출력 스트림을 파일로 쉽게 변경할 수 있습니다 : (: 복사 붙여 넣기 :)

#include <iostream>
#include <fstream>
using namespace std;

int main ()
{
    cout << "This is sent to prompt" << endl;
    ofstream file;
    file.open ("test.txt");
    streambuf* sbuf = cout.rdbuf();
    cout.rdbuf(file.rdbuf());
    cout << "This is sent to file" << endl;
    cout.rdbuf(sbuf);
    cout << "This is also sent to prompt" << endl;
    return 0;
}

3) 특히 매개 변수가 많을 때 cout이 더 읽기 쉽습니다.

한 가지 문제 와 함께 cout서식 옵션입니다. 데이터 포맷팅 (정밀도, 정당성 등) printf이 더 쉽습니다.


1
좋네요 외국 라이브러리 스레드에서 아무도이 방법으로 전역 cout을 수정하지 않는지 어떻게 알 수 있습니까?
vp_arth

1
다음 printf과 같이 바꾸면 파일로 쉽게 변경할 수 있습니다 fprintf.
CoffeeTableEspresso

5

여기에 언급되지 않은 두 가지 점이 중요하다고 생각합니다.

1) cout아직 STL을 사용하지 않는 경우 많은 수하물을 운송합니다. 객체 파일에 두 배 이상의 코드를 추가합니다 printf. 이것은에도 해당 string되며 이것이 내 문자열 라이브러리를 사용하는 주된 이유입니다.

2) cout오버로드 된 <<연산자를 사용하여 불행한 것으로 나타났습니다. <<의도 한 목적으로 연산자를 사용하는 경우 혼동을 일으킬 수 있습니다 (왼쪽 시프트). 나는 개인적으로 의도 한 용도에 접하는 목적으로 운영자에게 과부하를 걸고 싶지 않습니다.

결론 : 이미 STL을 사용하고 있다면 cout(및 string)을 사용합니다. 그렇지 않으면 피하는 경향이 있습니다.


4

프리미티브를 사용하면 사용하는 것이 중요하지 않을 수 있습니다. 유용한 곳은 복잡한 객체를 출력하고 싶을 때입니다.

예를 들어 수업이 있으면

#include <iostream>
#include <cstdlib>

using namespace std;

class Something
{
public:
        Something(int x, int y, int z) : a(x), b(y), c(z) { }
        int a;
        int b;
        int c;

        friend ostream& operator<<(ostream&, const Something&);
};

ostream& operator<<(ostream& o, const Something& s)
{
        o << s.a << ", " << s.b << ", " << s.c;
        return o;
}

int main(void)
{
        Something s(3, 2, 1);

        // output with printf
        printf("%i, %i, %i\n", s.a, s.b, s.c);

        // output with cout
        cout << s << endl;

        return 0;
}

위의 내용이 그다지 좋지는 않지만 코드의 여러 곳에서 이것을 출력해야한다고 가정 해 봅시다. 뿐만 아니라 "int d"필드를 추가한다고 가정 해 봅시다. cout을 사용하면 한 번만 변경하면됩니다. 그러나 printf를 사용하면 많은 곳에서 변경해야 할뿐만 아니라 출력 할 곳을 상기시켜야합니다.

cout을 사용하면 코드 유지 관리에 소요되는 시간을 크게 줄일 수 있으며 새 응용 프로그램에서 "Something"개체를 재사용 할 경우 출력에 대해 걱정할 필요가 없습니다.


또한 성능에 대해 추가하기 위해 응용 프로그램이 성능을 위해 만들어진 경우 아무것도 출력하지 않아야한다고 말하고 싶습니다. 표준 출력의 모든 종류는 다소 비싸고 느립니다. 나는 그것을 피하고 절대적으로 필요할 때만 출력해야한다고 말합니다.
Daniel

수업에 외부에서 쉽게 액세스 할 수없는 개인 회원이있을 수 있습니다. 출력 연산자를 사용하면 클래스와 친구가되어야하는 위치가 정확히 1 개이므로 알 수없는 코드로도 어디서나 출력 할 수 있습니다.
hochl

2

물론 유지 보수를 유지하기 위해 "무언가"를 조금 더 잘 작성할 수 있습니다.

#include <iostream>
#include <cstdlib>

using namespace std;

class Something
{
    public:
        Something(int x, int y, int z) : a(x), b(y), c(z) { }
        int a;
        int b;
        int c;

        friend ostream& operator<<(ostream&, const Something&);

        void print() const { printf("%i, %i, %i\n", a, b, c); }
};

ostream& operator<<(ostream& o, const Something& s)
{
    o << s.a << ", " << s.b << ", " << s.c;
    return o;
}

int main(void)
{
    Something s(3, 2, 1);

    // Output with printf
    s.print(); // Simple as well, isn't it?

    // Output with cout
    cout << s << endl;

    return 0;
}

cout과 printf에 대한 약간의 확장 테스트는 더 많은 테스트를 원한다면 'double'테스트를 추가했습니다 (Visual Studio 2008, 릴리스 버전의 실행 파일).

#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    //timespec d_start;
    clock_t d_start;

    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            //clock_gettime(CLOCK_REALTIME, &d_start);
            d_start = clock();
        }
        ~TimedSection() {
            clock_t end;
            //clock_gettime(CLOCK_REALTIME, &end);
            end = clock();
            double duration = /*1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
                              */
                              (double) (end - d_start) / CLOCKS_PER_SEC;

            std::cerr << d_name << '\t' << std::fixed << duration * 1000.0 << " ms\n";
        }
};


int main() {
    const int iters = 1000000;
    char const *text = "01234567890123456789";
    {
        TimedSection s("cout with only endl");
        for (int i = 0; i < iters; ++i)
            std::cout << std::endl;
    }
    {
        TimedSection s("cout with only '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << '\n';
    }
    {
        TimedSection s("printf with only '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("\n");
    }
    {
        TimedSection s("cout with string constant and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789" << std::endl;
    }
    {
        TimedSection s("cout with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789\n";
    }
    {
        TimedSection s("printf with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("01234567890123456789\n");
    }
    {
        TimedSection s("cout with some stuff and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << std::endl;
    }
    {
        TimedSection s("cout with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << '\n';
    }
    {
        TimedSection s("printf with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("%s01234567890123456789%i\n", text, i);
    }
    {
        TimedSection s("cout with formatted double (width & precision once)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;
        std::cout.width(8);
        for (int i = 0; i < iters; ++i)
            std::cout << text << 8.315 << i << '\n';
    }
    {
        TimedSection s("cout with formatted double (width & precision on each call)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;

        for (int i = 0; i < iters; ++i)
            { std::cout.width(8);
              std::cout.precision(3);
              std::cout << text << 8.315 << i << '\n';
            }
    }
    {
        TimedSection s("printf with formatted double");
        for (int i = 0; i < iters; ++i)
            printf("%8.3f%i\n", 8.315, i);
    }
}

결과는 다음과 같습니다.

cout with only endl    6453.000000 ms
cout with only '\n'    125.000000 ms
printf with only '\n'    156.000000 ms
cout with string constant and endl    6937.000000 ms
cout with string constant and '\n'    1391.000000 ms
printf with string constant and '\n'    3391.000000 ms
cout with some stuff and endl    9672.000000 ms
cout with some stuff and '\n'    7296.000000 ms
printf with some stuff and '\n'    12235.000000 ms
cout with formatted double (width & precision once)    7906.000000 ms
cout with formatted double (width & precision on each call)    9141.000000 ms
printf with formatted double    3312.000000 ms

와, 왜 endl그렇게 효율적이지 '\n'않습니까?
Nicholas Hamilton

1
나는 그것이 endl버퍼를 플러시 하기 때문이라고 생각 \n하지만 이것이 확실하지 않은 이유는 확실하지 않습니다.
Caleb Xu

이것은 질문에 대한 답이 아니며 DanielThomas의 답변과 비슷합니다 .
Fabio에 따르면 Reinstate Monica는

2

C ++에서 스레드로 재생하려면 사용 cout하면 흥미로운 결과를 얻을 수 있다고 지적하고 싶습니다 .

이 코드를 고려하십시오.

#include <string>
#include <iostream>
#include <thread>

using namespace std;

void task(int taskNum, string msg) {
    for (int i = 0; i < 5; ++i) {
        cout << "#" << taskNum << ": " << msg << endl;
    }
}

int main() {
    thread t1(task, 1, "AAA");
    thread t2(task, 2, "BBB");
    t1.join();
    t2.join();
    return 0;
}

// g++ ./thread.cpp -o thread.out -ansi -pedantic -pthread -std=c++0x

이제 출력이 모두 섞입니다. 다른 결과를 얻을 수도 있으므로 여러 번 실행하십시오.

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

당신이 사용할 수있는 printf것이 바로 얻기 위해, 또는 당신이 사용할 수 있습니다 mutex.

#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB

즐기세요!


2
wtf thread는 출력을 방해 하지 않습니다. 방금 결과물 xyz과 재생 산물을 모두 발견했습니다 ABC. 이 승 / B를 엉망으로되지 않았습니다 ABCABABAB.
Abhinav Gauniyal

1
나는 방법을 모른다 cout스레드와 작품,하지만 난 당신이 보여주고 코드가 당신이 그 출력을 얻기 위해 사용하는 사람이 아니라는 것을 확실히 알고있다. 코드는 문자열을 전달 "ABC"스레드 1과 "xyz"스레드 2,하지만 출력 쇼 AAABBB. 혼란 스럽기 때문에 문제를 해결하십시오.
Fabio는 Reinstate Monica가

1
cout<< "Hello";
printf("%s", "Hello"); 

둘 다 값을 인쇄하는 데 사용됩니다. 그것들은 완전히 다른 구문을 가지고 있습니다. C ++에는 둘 다 있고 C에는 printf 만 있습니다.


19
... 뭐? 뭔가 섞 었니?
xtofl

1
문제가 해결되었습니다. 고정이 필요하고 대답이 많이 필요하기 때문에 -1입니다.
Yacoby

3
함수 이름이 바뀌 었습니다. cout은 printf 구문과 함께 사용되었고 printf는 cout 구문과 함께 사용되었습니다. 받아 들여져서는 안됩니다!
Mahmoud Al-Qudsi

2
cout의 가장 큰 단점은 장황하고 추악하고 논란의 여지가있는 연산자 남용 인 operator <<를 사용한다는 것입니다. :)
jalf

8
이것이 최선의 대답은 아니지만 확실합니다. 나는 그것이 최선의 답변으로 선정 되었기 때문에 스카트 맨이 그의 대답에 대해 어떻게 처벌을 받는지 이해하지 못합니다. xbit는 IMO에 대한 답변이 좋지 않지만 -1 표를 받았습니다. 나는 xbit가 더 이상 투표를 받아야한다고 말하지는 않지만, OP의 실수로 인해 scatman을 투표에서 내리는 것이 더 이상 공정하지 않다고 생각합니다.
Jesse

1

확장 성 부족이 printf완전히 사실이 아니라고 말하고 싶습니다
. C에서는 사실입니다. 그러나 C에는 실제 클래스가 없습니다.
C ++에서는 캐스트 연산자를 오버로드하여 char*연산자를 오버로드하고 다음 과 printf같이 사용할 수 있습니다.

Foo bar;
...;
printf("%s",bar);

Foo가 좋은 작업자에게 과부하가 걸리면 가능할 수 있습니다. 또는 좋은 방법을 만든 경우. 요컨대, 나 printf만큼 확장 cout할 수 있습니다.

C ++ 스트림 (일반적으로 cout뿐만 아니라)에 대해 볼 수있는 기술적 인 주장은 다음과 같습니다.

  • 타입 안전. (그리고 그런데, 내가 하나를 인쇄 '\n'하려면 내가 사용하는 putchar('\n')... 나는 핵 폭탄을 사용하여 곤충을 죽이지 않을 것입니다.)

  • 배우기 더 간단합니다. (배우기위한 "복잡한"매개 변수 없음, 단지 사용 <<>>연산자)

  • 기본적으로 워크 std::string(위해 printf존재 std::string::c_str()하지만,에 scanf?)

를 들어 printfI 참조 :

  • 더 복잡하거나 더 짧은 (쓰기 된 문자로) 복잡한 형식입니다. 나를 위해 훨씬 더 읽기 쉽다 (맛이 나는 것 같아요).

  • 함수의 기능을보다 잘 제어 할 수 있습니다 (작성된 문자 수와 %n포맷터 가있는 경우 : "아무것도 인쇄되지 않습니다. 인수는 지금까지 작성된 문자 수가 저장되는 부호있는 int에 대한 포인터 여야합니다"( printf에서) -C ++ 레퍼런스 )

  • 더 나은 디버깅 가능성. 마지막 주장과 같은 이유로.

개인적으로 선호하는 기능은 주로 짧은 줄을 좋아하고 텍스트 인쇄시 유형 문제를 피하기 어렵다고 생각하기 때문에 printf(및 scanf) 기능으로 이동합니다 . C 스타일 함수로 내가 싫어하는 것은 std::string지원되지 않는다는 것입니다. 우리는 char*그것을주기 전에 겪어야 합니다 printf( std::string::c_str()읽기를 원한다면, 어떻게 쓰는가?)


3
컴파일러에는 varargs 함수에 대한 유형 정보가 없으므로 실제 매개 변수를 변환하지 않습니다 ( 표준 정수 승격과 같은 기본 인수 승격 제외 ). 5.2.2p7을 참조하십시오. 에 대한 사용자 정의 변환 char*은 사용되지 않습니다.
벤 Voigt

이것이 효과가 있더라도 스프린트의 확장 성이 아니라 스프린트를 기대할 수있는 영리한 해킹 일뿐 아니라 char*삶의 위치와 시간, 사용자 정의의 위험과 같은 심각한 문제는 무시합니다. 암시 적 캐스트.
Marcelo Cantos 2016 년

1

더 많은 차이점 : "printf"는 정수 값 (인쇄 된 문자 수와 같음)을 반환하고 "cout"은 아무것도 반환하지 않습니다

과.

cout << "y = " << 7; 원자가 아닙니다.

printf("%s = %d", "y", 7); 원자입니다.

cout은 유형 검사를 수행하지만 printf는 수행하지 않습니다.

이에 상응하는 iostream이 없습니다 "% d"


3
cout함수가 아닌 객체이기 때문에 아무것도 반환하지 않습니다. operator<<무언가를 반환합니다 (일반적으로 왼쪽 피연산자이지만 오류가 있으면 거짓 값). 그리고 어떤 의미에서 printf부름은 "원자"입니까?
Keith Thompson

9
그것은 원자 폭탄과 같습니다. printf("%s\n",7);
artless noise

@artlessnoise 왜 세분화 오류가 발생합니까? %s무엇입니까?
Abhinav Gauniyal

1
이것이 '원자 폭탄'진술의 요점입니다. printf %의 인수는 널 종료 문자열에 유효한 포인터가 있어야합니다. 메모리 범위 '7'(포인터)은 일반적으로 유효하지 않습니다. 분할 오류는 운이 좋을 수 있습니다. 일부 시스템에서 '7'은 많은 가비지를 콘솔에 인쇄 할 수 있으며 프로그램이 중지되기 하루 전에이를 확인해야합니다. 다시 말해, 이것은에 대한 나쁜 것 printf입니다. 정적 분석 도구는 이러한 많은 문제를 포착 할 수 있습니다.
artless noise

기술적으로 printf타입 검사를하지는 않지만 타입 오류에 대해 경고하지 않은 컴파일러를 사용한 적이 없습니다 printf.
CoffeeTableEspresso

1

TL; DR : 생성 된 머신 코드 크기 , 성능 , 가독성코딩 시간 과 관련하여 항상 자신의 연구를 수행하십시오 .

난 전문가가 아니야 방금 성능 문제로 인해 임베디드 시스템에서 C ++를 사용하지 않는 방법에 대해 이야기하는 두 명의 동료에 대해 이야기를 들었습니다. 글쎄, 흥미롭게도 실제 프로젝트 작업을 기반으로 벤치 마크를 수행했습니다.

이 작업에서는 RAM에 구성을 작성해야했습니다. 다음과 같은 것 :

커피 =
설탕 = 없음
우유 = 유방
맥 = AA : BB : CC : DD : EE : FF

여기 내 벤치 마크 프로그램이 있습니다 (예, OP는 fprintf ()가 아니라 printf ()에 대해 물었습니다. 본질을 포착하려고 노력하지만 OP의 링크는 fprintf ()를 가리 킵니다).

C 프로그램 :

char coffee[10], sugar[10], milk[10];
unsigned char mac[6];

/* Initialize those things here. */

FILE * f = fopen("a.txt", "wt");

fprintf(f, "coffee=%s\nsugar=%s\nmilk=%s\nmac=%02X:%02X:%02X:%02X:%02X:%02X\n", coffee, sugar, milk, mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]);

fclose(f);

C ++ 프로그램 :

//Everything else is identical except:

std::ofstream f("a.txt", std::ios::out);

f << "coffee=" << coffee << "\n";
f << "sugar=" << sugar << "\n";
f << "milk=" << milk << "\n";
f << "mac=" << (int)mac[0] << ":"
    << (int)mac[1] << ":"
    << (int)mac[2] << ":"
    << (int)mac[3] << ":"
    << (int)mac[4] << ":"
    << (int)mac[5] << endl;
f.close();

나는 그들을 100,000 번 반복하기 전에 연마하기 위해 최선을 다했습니다. 결과는 다음과 같습니다.

C 프로그램 :

real    0m 8.01s
user    0m 2.37s
sys     0m 5.58s

C ++ 프로그램 :

real    0m 6.07s
user    0m 3.18s
sys     0m 2.84s

객체 파일 크기 :

C   - 2,092 bytes
C++ - 3,272 bytes

결론 : 내 매우 구체적인에서 플랫폼 매우 구체적인와, 프로세서 의 매우 구체적인 버전 실행, 리눅스 커널 의 매우 구체적인 버전으로 컴파일 된 프로그램 실행, GCC를 매우 구체적인 달성하기 위해, 작업을 , 나는 말할 것 C ++ 접근 방식은 훨씬 빠르게 실행되고 훨씬 더 나은 가독성을 제공하기 때문에 더 적합합니다. 다른 한편으로, C는 작은 발자국을 제공한다고 생각합니다. 프로그램 크기가 우리의 관심사가 아니기 때문에 거의 아무것도 의미하지 않습니다.

리 메버, YMMV.


이 예제에서는 C ++가 더 읽기 쉽다는 것에 동의하지 않습니다. 예제는 여러 개의 행을 단일 printf 호출로 묶기 때문입니다. 그것은 자연스럽게 C ++ 코드를 읽는 방식보다 읽기 쉽지 않으며 읽기가 어렵고 유지 관리가 어렵 기 때문에 C에서는 거의 수행되지 않습니다. 공정한 비교는 C를 리치 라인을위한 별도의 printfs로 분산시킵니다.
maharvey67

1
당신이 한 말이 사실입니다. 그러나 C에서 제공 한 예는 성능을 고려한 것입니다. fprintf에 대한 통합형 호출은 이미 C ++ 동등성보다 2 초 느 렸습니다. C 코드를 읽을 수있게하려면 속도가 더 느려질 수 있습니다. 면책 조항 : 이것은 1 년 전이며 C 및 C ++ 코드를 모두 연마하기 위해 최선을 다했음을 기억합니다. fprintf에 대한 개별 호출이 하나의 단일 호출보다 빠를 것이라는 증거는 없었지만이 방법으로 수행 한 이유는 그렇지 않았 음을 나타냅니다.
웨슬리

0

저는 프로그래머는 아니지만 인적 요소 엔지니어였습니다. 프로그래밍 언어는 배우고 이해하고 사용하기 쉬워야한다고 생각하는데, 이는 단순하고 일관된 언어 구조를 가져야합니다. 모든 언어가 상징적이므로 핵심적으로 임의적이지만 규칙을 따르고 따라 가면 언어를 배우고 사용하기가 더 쉬워집니다.

C ++에는 함수 전 (pre-computer) 시대의 수학에서 기능적 관계에 원래 사용되었던 구문 인 function (parameter)으로 작성된 다른 언어가 많이 있습니다. printf()이 구문을 따르고 C ++ 작성자가 파일을 읽고 쓰는 논리적으로 다른 방법을 만들고자한다면 비슷한 구문을 사용하여 다른 기능을 만들었을 수 있습니다.

파이썬에서는 물론 object.method변수는 객체이지만 C ++에서는 그렇지 않기 때문에 상당히 표준적인 구문, 즉 variablename.print를 사용하여 인쇄 할 수 있습니다 .

<< 연산자는 규칙을 따르지 않기 때문에 cout 구문을 좋아하지 않습니다. 메소드 또는 함수입니다. 즉, 매개 변수를 사용하여 무언가를 수행합니다. 그러나 수학 비교 연산자 인 것처럼 작성되었습니다. 이것은 인적 요소 관점에서 볼 때 좋지 않은 접근법입니다.


-1

printfcout변수 이지만 함수 입니다.


6
답변 자체가 틀릴 수도 있지만 여전히 진정한 답변이므로 롤백을 수행했습니다. 답변이 잘못되었다고 생각하는 경우 1) 의견 추가 또는 2) 새 답변 추가 (또는 두 가지 모두)의 두 가지 옵션이 있습니다. 저자가 의도 한 것과 완전히 다른 것을 말하도록 누군가의 대답을 바꾸지 마십시오.
Mark

1
printf 함수이지만 printf() 함수 호출입니다 =)
vp_arth

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