답변:
이 질문의 모든 사람들이 질문이 단지 차이점을 요구하더라도. std::cout
보다 더 낫다고 주장하는 것에 놀랐습니다 printf
. 이제 차이점 std::cout
은 C ++ printf
이며 C입니다 (그러나 C의 거의 다른 것과 마찬가지로 C ++에서 사용할 수 있습니다 ). 이제 솔직히 말하겠습니다. 모두 printf
와 std::cout
그들의 장점이있다.
std::cout
확장 가능합니다. 사람들 printf
은 확장 할 수 있다고 말하지만 그러한 확장은 C 표준에 언급되어 있지 않으므로 비표준 기능을 사용해야하지만 일반적인 비표준 기능은 존재하지 않습니다. (이미 존재하는 형식과 충돌하기 쉽습니다).
달리 printf
, std::cout
연산자 오버로딩에 완전히 의존하므로 사용자 정의 형식과 아무 문제 없다 - 당신이 할 모든 서브 루틴 복용 정의입니다 std::ostream
첫 번째 인수로하고 두 번째와 같은 유형입니다. 따라서 네임 스페이스 문제는 없습니다. 클래스가 한 문자로 제한되지 않는 한 std::ostream
오버로드 작업을 수행 할 수 있습니다 .
그러나 많은 사람들이 확장하기를 원한다고 의심합니다 ostream
(솔직히 말하면 쉽게 확장 할 수는 없지만 그러한 확장을 거의 보지 못했습니다). 그러나 필요한 경우 여기에 있습니다.
그것은 쉽게 발견 할 수 있기 때문에, 모두 printf
와는 std::cout
다른 구문을 사용합니다. printf
패턴 문자열 및 가변 길이 인수 목록을 사용하여 표준 함수 구문을 사용합니다. 사실, printf
C가 가지고있는 이유입니다- 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/fprintf 및 http://en.cppreference.com/w/cpp/types/integer 에서 사용 가능 합니다.
\0
때문에 printf
C ++ 문자열이 아닌 용도의 C 문자열이 특정 트릭없이 NUL 바이트를 인쇄 할 수 없습니다. 어떤 경우에는 그것을 사용하는 것이 가능 %c
하여 '\0'
그 분명히 해킹 있지만 인수로.
업데이트 : iostream
속도가 너무 느려 일반적으로 하드 드라이브보다 느립니다 (프로그램을 파일로 리디렉션하는 경우). stdio
많은 양의 데이터를 출력해야하는 경우 동기화를 비활성화 하면 도움 이 될 수 있습니다. STDOUT에 여러 줄을 쓰는 것과는 대조적으로 성능이 실제로 문제가되는 경우을 사용하십시오 printf
.
누구나 성능에 관심이 있다고 생각하지만 아무도 그것을 측정 할 필요가 없습니다. 내 대답은 당신이 사용하는 경우 I / O는, 어쨌든 병목 현상없이하다는 것이다 printf
나 iostream
. 어셈블리를 빠르게 살펴보면 컴파일러 옵션 이 더 빠를 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);
^
std::sort
에 비해 다소 놀라 울 정도로 빠른 인 qsort
에서 2 회 () 실행 파일 크기 비용).
로부터 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::ostream
and와 같은 실제 클래스에서 빌드됩니다std::istream
. 달리<cstdio>
의 'FILE*
이 실제 수업 따라서 상속합니다. 즉, 스트림처럼 보이고 작동하는 다른 사용자 정의 항목을 가질 수 있지만 원하는 이상하고 멋진 작업을 수행 할 수 있습니다. 모르는 사용자가 작성한 수백만 줄의 I / O 코드를 자동으로 사용할 수 있으며 "확장 스트림"클래스에 대해 알 필요가 없습니다.
반면에, printf
에 우선을 사용하여 정당화 할 수있는, 상당히 빠른 cout
에서 매우 제한적인 특정 경우. 항상 먼저 프로파일하십시오. (예를 들어 http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout / 참조)
printf()
도 확장 가능해야합니다. udrepper.livejournal.com/20948.html의
printf
에는 그런 능력이 없습니다. 이식 불가능한 라이브러리 메커니즘은 완전히 표준화 된 iostream 확장 성과 같은 수준에 있지 않습니다.
사람들은 종종 그것이 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
거의 빠르거나 훨씬 빠릅니다. 자세한 내용은 내 블로그 에서 확인할 수 있습니다 .
분명히하기 위해 iostream
s가 항상보다 낫다고 말하려고하지는 않습니다 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);
}
}
printf()
및 std::ostream
하다 전 출력 단일 호출 모든 인수 반면 std::ostream
초래 각각에 대해 개별적으로 호출 <<
. 테스트는 하나의 인수와 줄 바꾸기 만 출력하므로 차이를 볼 수 없습니다.
printf
다양한 형식 지정자를 위해 도우미 함수를 다루기 위해 표지 아래에서 많은 호출을 할 수 있습니다. 그리고 다시 인라인으로 인해 속도에 전혀 차이가 없어야합니다.
sprintf
또는 fprintf
및 stringstream
나 fstream
.
그리고 나는 인용한다 :
높은 수준의 용어의 주요 차이점은 형식 안전성 (cstdio에없는 것), 성능 (대부분의 iostream 구현이 cstdio보다 느리다) 및 확장 성 (iostream은 사용자 정의 출력 대상 및 사용자 정의 유형의 원활한 출력 허용)입니다.
나를 위해, '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
이 더 쉽습니다.
printf
과 같이 바꾸면 파일로 쉽게 변경할 수 있습니다 fprintf
.
여기에 언급되지 않은 두 가지 점이 중요하다고 생각합니다.
1) cout
아직 STL을 사용하지 않는 경우 많은 수하물을 운송합니다. 객체 파일에 두 배 이상의 코드를 추가합니다 printf
. 이것은에도 해당 string
되며 이것이 내 문자열 라이브러리를 사용하는 주된 이유입니다.
2) cout
오버로드 된 <<
연산자를 사용하여 불행한 것으로 나타났습니다. <<
의도 한 목적으로 연산자를 사용하는 경우 혼동을 일으킬 수 있습니다 (왼쪽 시프트). 나는 개인적으로 의도 한 용도에 접하는 목적으로 운영자에게 과부하를 걸고 싶지 않습니다.
결론 : 이미 STL을 사용하고 있다면 cout
(및 string
)을 사용합니다. 그렇지 않으면 피하는 경향이 있습니다.
프리미티브를 사용하면 사용하는 것이 중요하지 않을 수 있습니다. 유용한 곳은 복잡한 객체를 출력하고 싶을 때입니다.
예를 들어 수업이 있으면
#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"개체를 재사용 할 경우 출력에 대해 걱정할 필요가 없습니다.
물론 유지 보수를 유지하기 위해 "무언가"를 조금 더 잘 작성할 수 있습니다.
#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'
않습니까?
endl
버퍼를 플러시 하기 때문이라고 생각 \n
하지만 이것이 확실하지 않은 이유는 확실하지 않습니다.
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
즐기세요!
thread
는 출력을 방해 하지 않습니다. 방금 결과물 xyz
과 재생 산물을 모두 발견했습니다 ABC
. 이 승 / B를 엉망으로되지 않았습니다 ABC
등 ABABAB
.
cout
스레드와 작품,하지만 난 당신이 보여주고 코드가 당신이 그 출력을 얻기 위해 사용하는 사람이 아니라는 것을 확실히 알고있다. 코드는 문자열을 전달 "ABC"
스레드 1과 "xyz"
스레드 2,하지만 출력 쇼 AAA
와 BBB
. 혼란 스럽기 때문에 문제를 해결하십시오.
cout<< "Hello";
printf("%s", "Hello");
둘 다 값을 인쇄하는 데 사용됩니다. 그것들은 완전히 다른 구문을 가지고 있습니다. C ++에는 둘 다 있고 C에는 printf 만 있습니다.
확장 성 부족이 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
?)
를 들어 printf
I 참조 :
더 복잡하거나 더 짧은 (쓰기 된 문자로) 복잡한 형식입니다. 나를 위해 훨씬 더 읽기 쉽다 (맛이 나는 것 같아요).
함수의 기능을보다 잘 제어 할 수 있습니다 (작성된 문자 수와 %n
포맷터 가있는 경우 : "아무것도 인쇄되지 않습니다. 인수는 지금까지 작성된 문자 수가 저장되는 부호있는 int에 대한 포인터 여야합니다"( printf에서) -C ++ 레퍼런스 )
더 나은 디버깅 가능성. 마지막 주장과 같은 이유로.
개인적으로 선호하는 기능은 주로 짧은 줄을 좋아하고 텍스트 인쇄시 유형 문제를 피하기 어렵다고 생각하기 때문에 printf
(및 scanf
) 기능으로 이동합니다 . C 스타일 함수로 내가 싫어하는 것은 std::string
지원되지 않는다는 것입니다. 우리는 char*
그것을주기 전에 겪어야 합니다 printf
( std::string::c_str()
읽기를 원한다면, 어떻게 쓰는가?)
char*
은 사용되지 않습니다.
char*
삶의 위치와 시간, 사용자 정의의 위험과 같은 심각한 문제는 무시합니다. 암시 적 캐스트.
더 많은 차이점 : "printf"는 정수 값 (인쇄 된 문자 수와 같음)을 반환하고 "cout"은 아무것도 반환하지 않습니다
과.
cout << "y = " << 7;
원자가 아닙니다.
printf("%s = %d", "y", 7);
원자입니다.
cout은 유형 검사를 수행하지만 printf는 수행하지 않습니다.
이에 상응하는 iostream이 없습니다 "% d"
cout
함수가 아닌 객체이기 때문에 아무것도 반환하지 않습니다. operator<<
무언가를 반환합니다 (일반적으로 왼쪽 피연산자이지만 오류가 있으면 거짓 값). 그리고 어떤 의미에서 printf
부름은 "원자"입니까?
printf("%s\n",7);
%s
무엇입니까?
printf
%의 인수는 널 종료 문자열에 유효한 포인터가 있어야합니다. 메모리 범위 '7'(포인터)은 일반적으로 유효하지 않습니다. 분할 오류는 운이 좋을 수 있습니다. 일부 시스템에서 '7'은 많은 가비지를 콘솔에 인쇄 할 수 있으며 프로그램이 중지되기 하루 전에이를 확인해야합니다. 다시 말해, 이것은에 대한 나쁜 것 printf
입니다. 정적 분석 도구는 이러한 많은 문제를 포착 할 수 있습니다.
printf
타입 검사를하지는 않지만 타입 오류에 대해 경고하지 않은 컴파일러를 사용한 적이 없습니다 printf
.
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 ++에는 함수 전 (pre-computer) 시대의 수학에서 기능적 관계에 원래 사용되었던 구문 인 function (parameter)으로 작성된 다른 언어가 많이 있습니다. printf()
이 구문을 따르고 C ++ 작성자가 파일을 읽고 쓰는 논리적으로 다른 방법을 만들고자한다면 비슷한 구문을 사용하여 다른 기능을 만들었을 수 있습니다.
파이썬에서는 물론 object.method
변수는 객체이지만 C ++에서는 그렇지 않기 때문에 상당히 표준적인 구문, 즉 variablename.print를 사용하여 인쇄 할 수 있습니다 .
<< 연산자는 규칙을 따르지 않기 때문에 cout 구문을 좋아하지 않습니다. 메소드 또는 함수입니다. 즉, 매개 변수를 사용하여 무언가를 수행합니다. 그러나 수학 비교 연산자 인 것처럼 작성되었습니다. 이것은 인적 요소 관점에서 볼 때 좋지 않은 접근법입니다.