C++ 코드에서는 어떤 C I/O 라이브러리를 사용해야 합니까?[닫은]

StackOverflow https://stackoverflow.com/questions/119098

  •  02-07-2019
  •  | 
  •  

문제

새로운 C++ 코드에서는 C stdio 라이브러리 대신 C++ iostream 라이브러리를 사용하는 경향이 있습니다.

나는 일부 프로그래머들이 stdio를 고수하면서 그것이 이식성이 더 좋다고 주장하는 것을 보았습니다.

이것이 정말로 사실입니까?무엇을 사용하는 것이 더 낫습니까?

도움이 되었습니까?

해결책

원래 질문에 대답하려면 다음을 수행하십시오.
stdio를 사용하여 수행할 수 있는 모든 작업은 iostream 라이브러리를 사용하여 수행할 수 있습니다.

Disadvantages of iostreams: verbose
Advantages    of iostreams: easy to extend for new non POD types.

C를 통해 이루어진 C++의 발전은 유형 안전성이었습니다.

  • iostreams는 명시적으로 유형이 안전하도록 설계되었습니다.따라서 개체에 대한 할당은 할당되는 개체의 유형(컴파일러 시간에)도 명시적으로 확인했습니다(필요한 경우 컴파일 시간 오류 생성).따라서 런타임 메모리 오버런이나 char 객체 등에 float 값을 쓰는 것을 방지합니다.

  • 반면에 scanf()/printf() 및 계열은 프로그래머가 올바른 형식 문자열을 얻는 데 의존하며 유형 검사가 없었습니다(gcc에 도움이 되는 확장이 있다고 생각합니다).결과적으로 이는 많은 버그의 원인이 되었습니다(프로그래머는 컴파일러보다 분석에 있어 덜 완벽하기 때문입니다[컴파일러가 단지 인간보다 완벽하다고 말할 수는 없습니다]).

Colin Jensen의 의견을 명확히 하기 위해.

  • iostream 라이브러리는 마지막 표준 출시 이후 안정적이었습니다(실제 연도는 잊어버렸지만 약 10년 전입니다).

Mikael Jansson의 의견을 명확히 합니다.

  • 그가 언급한 형식 스타일을 사용하는 다른 언어에는 (C에서는 언급된 언어는 제외) 런타임 충돌을 일으킬 수 있는 C stdio 라이브러리의 위험한 부작용을 방지하기 위한 명시적인 보호 장치가 있습니다.

NB 나는 iostream 라이브러리가 약간 장황한 측면에 있다는 데 동의합니다.그러나 런타임 안전을 보장하기 위해 장황한 내용을 참을 의향이 있습니다.그러나 우리는 다음을 사용하여 자세한 내용을 완화할 수 있습니다. 부스트 형식 라이브러리.

#include <iostream>
#include <iomanip>
#include <boost/format.hpp>

struct X
{  // this structure reverse engineered from
   // example provided by 'Mikael Jansson' in order to make this a running example

    char*       name;
    double      mean;
    int         sample_count;
};
int main()
{
    X   stats[] = {{"Plop",5.6,2}};

    // nonsense output, just to exemplify

    // stdio version
    fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
            stats, stats->name, stats->mean, stats->sample_count);

    // iostream
    std::cerr << "at " << (void*)stats << "/" << stats->name
              << ": mean value " << std::fixed << std::setprecision(3) << stats->mean
              << " of " << std::setw(4) << std::setfill(' ') << stats->sample_count
              << " samples\n";

    // iostream with boost::format
    std::cerr << boost::format("at %p/%s: mean value %.3f of %4d samples\n")
                % stats % stats->name % stats->mean % stats->sample_count;
}

다른 팁

너무 장황합니다.

다음을 수행하기 위한 iostream 구성을 숙고하십시오(scanf의 경우와 유사).

// nonsense output, just to examplify
fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
    stats, stats->name, stats->mean, stats->sample_count);

이를 위해서는 다음과 같은 것이 필요합니다:

std::cerr << "at " << static_cast<void*>(stats) << "/" << stats->name
          << ": mean value " << std::precision(3) << stats->mean
          << " of " << std::width(4) << std::fill(' ') << stats->sample_count
          << " samples " << std::endl;

문자열 형식화는 문자열에 포함된 형식화 DSL을 선호하여 객체 지향성을 회피할 수 있고 회피해야 하는 경우입니다.Lisp를 고려해보세요 format, Python의 printf 스타일 형식화 또는 PHP, Bash, Perl, Ruby 및 해당 문자열 삽입.

iostream 해당 사용 사례는 기껏해야 잘못된 것입니다.

그만큼 부스트 형식 라이브러리 printf 스타일 문자열 형식화에 대해 유형 안전하고 객체 지향적인 대안을 제공하며, 연산자%의 영리한 사용으로 인해 일반적인 장황한 문제를 겪지 않는 iostream을 보완합니다.iostream의 연산자<<로 포맷하는 것을 싫어한다면 일반 C printf를 사용하는 것보다 이를 고려하는 것이 좋습니다.

좋지 않은 옛날에는 C++ 표준 위원회가 계속해서 언어 문제를 다루었고 iostreams는 움직이는 목표였습니다.iostream을 사용했다면 매년 코드의 일부를 다시 작성할 수 있는 기회가 주어졌습니다.이 때문에 나는 1989년 이후로 크게 변하지 않은 stdio를 항상 사용했습니다.

오늘 작업을 한다면 iostream을 사용하겠습니다.

나처럼 C++를 배우기 전에 C를 배웠다면 stdio 라이브러리를 사용하는 것이 더 자연스러워 보일 것입니다.iostream과 iostream의 장단점이 있습니다.stdio이지만 iostream을 사용할 때 printf()가 누락되었습니다.

원칙적으로 나는 iostream을 사용할 것입니다. 실제로는 iostream을 읽을 수 없게 만드는 너무 많은 형식의 소수점 등을 수행하므로 stdio를 사용합니다.Boost::format은 개선되었지만 나에게 충분히 동기를 부여하지는 않습니다.실제로 stdio는 대부분의 최신 컴파일러가 인수 검사를 수행하므로 거의 형식이 안전합니다.

아직 어떤 솔루션에도 완전히 만족하지 못하는 부분입니다.

바이너리 IO의 경우 stdio의 fread 및 fwrite를 사용하는 경향이 있습니다.형식화된 항목의 경우 일반적으로 IO Stream을 사용하지만 Mikael이 말했듯이 중요하지 않은(기본값이 아닌) 형식은 PITA가 될 수 있습니다.

C++ 표준 라이브러리의 두 주류 라이브러리를 비교해 보겠습니다.

C++에서는 C 스타일 형식 문자열 기반 문자열 처리 루틴을 사용하면 안 됩니다.

사용을 제한하는 데에는 몇 가지 이유가 있습니다.

  • 형식이 안전하지 않음
  • 비 POD 유형을 Variadic 인수 목록 (즉, Scanf+Co., 또는 printf+co.)에 전달할 수 없거나 정의되지 않은 행동의 어두운 거점에 들어갑니다.
  • 틀리기 쉬움:
    • 형식 문자열과 "값 인수 목록"을 동기화 상태로 유지해야 합니다.
    • 동기화를 유지해야 합니다 바르게

원격지에서 발생하는 미묘한 버그

printf 자체가 좋지 않은 것은 아닙니다.소프트웨어는 오래되고 리팩터링 및 수정되며 원격지에서 오류가 발생할 수 있습니다.당신이 가지고 있다고 가정

.

// foo.h
...
float foo;
...

그리고 어딘가에 ...

// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...

그리고 3년 후에는 foo가 사용자 정의 유형이어야 한다는 것을 알게 됩니다.

// foo.h
...
FixedPoint foo;
...

하지만 어딘가에 ...

// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...

...그러면 이전 printf/scanf는 여전히 컴파일됩니다. 단, 이제 무작위 세그폴트가 발생하고 그 이유를 기억하지 못한다는 점만 제외하면 됩니다.

iostream의 자세한 정보

printf()가 덜 장황하다고 생각한다면 iostream의 모든 기능을 사용하지 않을 가능성이 있습니다.예:

  printf ("My Matrix: %f %f %f %f\n"
          "           %f %f %f %f\n"
          "           %f %f %f %f\n"
          "           %f %f %f %f\n",
          mat(0,0), mat(0,1), mat(0,2), mat(0,3), 
          mat(1,0), mat(1,1), mat(1,2), mat(1,3), 
          mat(2,0), mat(2,1), mat(2,2), mat(2,3), 
          mat(3,0), mat(3,1), mat(3,2), mat(3,3));

iostream을 사용하는 것과 비교해 보세요.

cout << mat << '\n';

대략적인 printf의 구조를 갖는 연산자<<에 대한 적절한 오버로드를 정의해야 하지만 중요한 차이점은 이제 재사용 가능하고 유형이 안전한 것을 갖게 되었다는 것입니다.물론 printf와 유사한 것을 재사용할 수 있도록 만들 수도 있지만 그러면 다시 printf를 갖게 됩니다. (행렬 멤버를 새로운 멤버로 교체하면 어떻게 될까요?) FixedPoint?), 기타 사소하지 않은 사항은 제외합니다.FILE* 핸들을 전달해야 합니다.

C 스타일 형식 문자열은 iostream보다 I18N에 좋지 않습니다.

형식 문자열은 종종 국제화를 통해 구출되는 것으로 생각되지만 그 점에서는 iostream보다 전혀 낫지 않습니다.

printf ("Guten Morgen, Sie sind %f Meter groß und haben %d Kinder", 
        someFloat, someInt);

printf ("Good morning, you have %d children and your height is %f meters",
        someFloat, someInt); // Note: Position changed.

// ^^ not the best example, but different languages have generally different
//    order of "variables"

즉, 이전 스타일의 C 형식 문자열에는 iostream만큼 위치 정보가 부족합니다.

고려해 볼 수도 있습니다. 부스트::형식, 형식 문자열의 위치를 ​​명시적으로 명시하는 지원을 제공합니다.예제 섹션에서:

cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.

일부 printf 구현은 위치 인수를 제공하지만 비표준입니다.

내가해야합니까 절대 C 스타일 형식 문자열을 사용합니까?

성능(Jan Hudec이 지적한 대로) 외에는 이유가 없습니다.하지만 명심하세요:

“약 97%의 경우 작은 효율성은 잊어야 합니다.성급한 최적화는 모든 악의 근원입니다.하지만 우리는 그 중요한 3%에 대한 기회를 놓쳐서는 안 됩니다.훌륭한 프로그래머는 그러한 추론으로 안주하지 않을 것이며, 중요한 코드를 주의 깊게 살펴보는 것이 현명할 것입니다.하지만 해당 코드가 식별된 후에만 가능합니다.” - Knuth

그리고

"병목 현상은 놀라운 곳에서 발생하므로 병목 현상이있는 곳인 것으로 입증 될 때까지 두 번째 추측을 시도하고 속도 해킹을 시도하지 마십시오." - 파이크

예, printf 구현은 일반적으로 iostream보다 빠릅니다. 일반적으로 Boost::format보다 빠릅니다(제가 작성한 작고 구체적인 벤치마크에 따르면, 특히 상황에 따라 크게 달라져야 합니다:printf=100%이면 iostream=160%, Boost::format=220%)

그러나 그것에 대해 생각하는 것을 맹목적으로 생략하지 마십시오.얼마나 시간이 있나요? 정말 텍스트 처리에 돈을 쓰나요?프로그램이 종료되기까지 얼마나 오래 실행됩니까?C 스타일 형식 문자열, 느슨한 유형 안전성, 리팩토링 가능성 감소, 수년간 자신을 숨길 수 있고 고객이 직면 한 즐겨 찾기에만 자신을 드러낼 수있는 매우 미묘한 버그의 확률을 높이는 것이 전혀 관련이 있습니까?

개인적으로 20% 이상의 속도 향상을 얻을 수 없다면 물러서지 않을 것입니다.그러나 내 응용 프로그램은 문자열 처리보다 다른 작업에 거의 모든 시간을 소비하기 때문에 절대 필요하지 않았습니다.내가 쓴 일부 파서는 문자열 처리에 거의 모든 시간을 보내지 만 총 런타임은 너무 작아서 테스트 및 검증 노력의 가치가 없습니다.

몇 가지 수수께끼

마지막으로 몇 가지 수수께끼를 미리 설정하고 싶습니다.

컴파일러는 오류를 모두 찾아내지 않기 때문에 모든 오류를 찾습니다(그가 친절할 경우에만 제안할 수 있습니다).

shared_ptr<float> f(new float);
fscanf (stdout, "%u %s %f", f)

다른 게 없다면, 이게 무슨 문제인가요?

const char *output = "in total, the thing is 50%"
                     "feature  complete";
printf (output);

C++ iostreams API에는 많은 이점이 있지만 한 가지 중요한 문제는 i18n에 관한 것입니다.문제는 매개변수 대체 순서가 문화권에 따라 달라질 수 있다는 것입니다.전형적인 예는 다음과 같습니다.

// i18n UNSAFE 
std::cout << "Dear " << name.given << ' ' << name.family << std::endl;

영어에서는 작동하지만 중국어에서는 성이 먼저 나옵니다.

해외 시장을 위해 코드를 번역할 때 스니펫을 번역하는 것은 위험하기 때문에 새로운 l10ns에서는 단순히 문자열만 변경하는 것이 아니라 코드를 변경해야 할 수도 있습니다.

Boost::format은 stdio(매개변수를 다른 순서로 사용할 수 있는 단일 형식 문자열)와 iostream(유형 안전성, 확장성)의 장점을 결합한 것으로 보입니다.

나는 iostream을 사용하는데, 그 이유는 나중에 (필요한 경우) 스트림을 조작하기가 더 쉽기 때문입니다.예를 들어, 일부 추적 창에 출력을 표시하려고 한다는 것을 알 수 있습니다. 이는 cout 및 cerr을 사용하여 비교적 쉽게 수행할 수 있습니다.물론 유닉스에서도 파이프나 기타 것들을 조작할 수 있지만 이식성이 좋지는 않습니다.

나는 printf와 같은 형식을 좋아하기 때문에 일반적으로 문자열을 먼저 형식화한 다음 버퍼로 보냅니다.Qt에서는 자주 사용합니다. QString::스프린트f (그들은 사용을 권장하지만 QString::arg 대신에).나는 보았다 부스트.포맷 뿐만 아니라 구문에 실제로 익숙해지지 않았습니다(%가 너무 많음).그래도 정말 한번 살펴 봐야 겠어요.

iolibraries에 대해 내가 놓친 것은 형식화된 입력입니다.

iostreams에는 scanf()를 복제하는 좋은 방법이 없으며 심지어 Boost에도 입력에 필요한 확장이 없습니다.

stdio는 바이너리 파일을 읽는 데 더 좋습니다(블록을 벡터<unsigned char>로 읽고 .resize() 등을 사용하는 등).file.hh의 read_rest 함수를 참조하세요. http://nuwen.net/libnuwen.html 예를 들어.

C++ 스트림은 바이너리 파일을 읽을 때 많은 바이트로 인해 잘못된 eof가 발생할 수 있습니다.

iostream이 표준이 되었기 때문에 코드가 최신 버전의 컴파일러에서 확실히 작동한다는 것을 알고 이를 사용해야 합니다.요즘 대부분의 컴파일러는 iostream에 대해 잘 알고 있으므로 사용하는 데 아무런 문제가 없을 것 같습니다.

하지만 *printf 기능을 계속 사용하고 싶다면 내 의견으로는 문제가 없을 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top