std :: string이있는 printf?


157

내 이해는 네임 스페이스 string의 멤버 std이므로 왜 다음이 발생합니까?

#include <iostream>

int main()
{
    using namespace std;

    string myString = "Press ENTER to quit program!";
    cout << "Come up and C++ me some time." << endl;
    printf("Follow this command: %s", myString);
    cin.get();

    return 0;
}

여기에 이미지 설명을 입력하십시오

프로그램이 실행될 때마다 myString위 출력과 같이 겉으로 보이는 임의의 3 자 문자열이 인쇄 됩니다 .


8
많은 사람들이 그 책을 비판 합니다. 객체 지향 프로그래밍에 대해서는별로 없기 때문에 이해할 수 있지만 사람들이 주장하는 것만 큼 나쁘지 않다고 생각합니다.
Jesse Good

우프! 글쎄, 내가 책을 통해 나아갈 때 이것을 명심하는 것이 좋습니다. 내가 너무 많은 damange하지 않습니다 희망 그래서 나는 :), 그것은 내가 내년 정도의 과정을 읽어 것이다 유일한 C ++ 책되지 않습니다 확신
TheDarkIn1978

gcc로 컴파일 할 때 가장 높은 컴파일러 경고를 사용하면 질문에 대답 할 수 있습니다 . MSVC 가이를 처리하는 방법-모르겠습니다.
피터 VARGA

답변:


237

printfC 의미 1 에서 변수 인수를 사용 하기 때문에 형식이 안전하지 않기 때문에 컴파일 중 입니다. printf에 대한 옵션이 없으며 std::stringC 스타일 문자열 만 있습니다. 예상 한 것 대신 다른 것을 사용하면 원하는 결과를 얻지 못할 것입니다. 실제로 정의되지 않은 동작이므로 모든 일이 발생할 수 있습니다.

가장 쉬운 방법은 C를 사용하고 있기 때문에 ++, 정상적으로를 인쇄하고,이 문제를 해결하기 std::cout때문에, std::string를 통해 운영자가 오버로드 것을 지원 :

std::cout << "Follow this command: " << myString;

어떤 이유로, 당신은 C 스타일의 문자열을 추출해야하는 경우 사용할 수있는 c_str()방법 std::string의 GET에 const char *null로 끝나는입니다를. 귀하의 예를 사용하여 :

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

int main()
{
    using namespace std;

    string myString = "Press ENTER to quit program!";
    cout << "Come up and C++ me some time." << endl;
    printf("Follow this command: %s", myString.c_str()); //note the use of c_str
    cin.get();

    return 0;
}

printf안전하지만 형식이 안전한 함수를 원한다면 variadic 템플릿 (C ++ 11, MSVC12부터 모든 주요 컴파일러에서 지원됨)을 살펴보십시오. 여기 에서 하나의 예를 찾을 수 있습니다 . 표준 라이브러리에는 구현 된 것과 같은 것이 없지만 Boost에는 특히 boost::format있습니다.


[1] : 이는 여러 개의 인수를 전달할 수 있지만 함수는 해당 인수의 수와 유형을 알려줍니다. 의 경우 이는 의미 printf와 같은 인코딩 된 유형 정보가있는 문자열을 %d의미 int합니다. 유형이나 숫자에 대해 거짓말을하면 함수에 표준적인 방법이 없지만 일부 컴파일러는 거짓말을 할 때 경고를 확인하고 경고 할 수 있습니다.


@MooingDuck, 좋은 지적입니다. 그것은 Jerry의 대답에 있지만, 받아 들여지는 대답이기 때문에 사람들이 보는 것이므로 다른 사람들을보기 전에 떠날 수도 있습니다. 첫 번째 솔루션이되도록 권장되는 옵션을 추가했습니다.
chris jan

43

사용하지 마십시오 printf("%s", your_string.c_str());

cout << your_string;대신 사용하십시오 . 짧고 간단하며 형식이 안전합니다. 실제로 C ++를 작성할 때 일반적으로 printf완전히 피하는 것이 좋습니다. C ++에서 거의 필요하지 않거나 유용한 C의 남은 부분입니다.

에 관해서는 사용해야하는 cout대신 printf, 이유는 많다. 다음은 가장 명백한 몇 가지 예입니다.

  1. 질문에서 알 수 있듯이 printf유형 안전하지 않습니다. 전달하는 유형이 변환 지정자에서 제공된 유형과 다른 printf경우, 지정된 유형 인 것처럼 스택에서 찾은 것을 사용하여 정의되지 않은 동작을 제공합니다. 일부 컴파일러는 어떤 상황에서는 이에 대해 경고 할 수 있지만 일부 컴파일러는 전혀 또는 전혀 할 수 없으며 모든 상황에서 사용할 수는 없습니다.
  2. printf확장 할 수 없습니다. 기본 유형 만 전달할 수 있습니다. 이해하는 일련의 변환 지정자는 구현시 하드 코딩되어 있으므로 더 많은 것을 추가 할 수있는 방법이 없습니다. 잘 작성된 C ++는 주로 이러한 유형을 사용하여 해결중인 문제를 지향하는 유형을 구현해야합니다.
  3. 적절한 형식 지정이 훨씬 어렵습니다. 명백한 예를 들어, 사람들이 읽을 수 있도록 숫자를 인쇄 할 때는 일반적으로 몇 자리마다 수천 개의 구분 기호를 삽입하려고합니다. 정확한 자릿수와 구분 기호로 사용되는 문자는 다양하지만 cout그 숫자도 다룹니다. 예를 들면 다음과 같습니다.

    std::locale loc("");
    std::cout.imbue(loc);
    
    std::cout << 123456.78;

    이름없는 로케일 ( "")은 사용자 구성에 따라 로케일을 선택합니다. 따라서 내 컴퓨터 (미국 영어 용으로 구성됨)에서로 인쇄됩니다 123,456.78. 독일을 위해 컴퓨터를 구성한 사람에게는 다음과 같이 인쇄됩니다 123.456,78. 인도 용으로 구성된 사람에게는 다음과 같이 인쇄됩니다 1,23,456.78(물론 다른 사람도 많이 있습니다). 로 printfI 정확히 하나 개의 결과를 얻을 : 123456.78. 일관성은 있지만 모든 사람에게 지속적으로 잘못 됩니다. 본질적으로 문제를 해결하는 유일한 방법은 서식을 별도로 수행 한 다음 결과를 문자열로 전달하는 것입니다. 그 자체로는 단순히 작업이 올바르게 수행 되지printf 않기 때문 입니다.printf

  4. 그것들은 상당히 작지만 printf형식 문자열은 읽을 수 없을 수 있습니다. 심지어 사용하는 C 프로그래머들 사이에서 printf매일 실제로, 나는 적어도 99 %는 무엇을해야 할 일을 찾아 볼 필요가 추측에는 요 #%#x수단과 방법을 무엇으로부터 다릅니다 #에서 %#f예, 그들은 완전히 다른 의미 수단 (그리고 ).

11
@ TheDarkIn1978 : 아마 잊어 버렸습니다 #include <string>. VC ++에는 헤더를 cout포함하지 않고 문자열을 정의 할 수는 있지만 문자열을 보낼 수없는 이상한 점이 있습니다 <string>.
Jerry Coffin 2016 년

28
@ 제리 : 단지 큰 데이터를 다룰 때 printf를 사용하는 것이 cout을 사용하는 것보다 훨씬 빠르다는 점을 지적하고 싶습니다. 따라서 쓸모가 없다고 말하지 마십시오. : D
Programmer

7
@Programmer : stackoverflow.com/questions/12044357/…를 참조하십시오 . 요약 : 대부분의 경우 cout속도가 느려집니다 std::endl. 사용 하지 말아야 할 곳을 사용했기 때문 입니다.
Jerry Coffin

29
전형적인 C ++ 전문가의 오만. printf가 존재한다면 왜 사용하지 않습니까?
kuroi neko

6
알겠습니다. 여전히 printf는 디버깅에 매우 편리하며 스트림은 훨씬 강력하지만 코드가 실제 출력에 대한 아이디어를 제공하지 못한다는 단점이 있습니다. 형식화 된 출력의 경우 printf는 여전히 실행 가능한 대안이며 두 시스템이 더 잘 협력 할 수없는 것은 부끄러운 일입니다. 물론 제 의견입니다.
kuroi neko

28

사용 myString.c_str()당신이 C와 같은 문자열을 (원하는 경우 const char*printf와 함께 사용하기에)

감사



1

주된 이유는 아마도 C ++ 문자열이 0 바이트로 끝나는 일련의 문자 주소뿐만 아니라 현재 길이 값을 포함하는 구조체이기 때문일 것입니다. Printf와 그 친척들은 구조체가 아닌 이러한 시퀀스를 찾을 것으로 기대하므로 C ++ 문자열에 혼동됩니다.

나 자신을 말하자면, printf에는 html의 테이블 구조가 div로 쉽게 채울 수없는 장소와 마찬가지로 C ++ 구문 기능으로 쉽게 채울 수없는 장소가 있다고 생각합니다. Dykstra가 나중에 goto에 대해 썼을 때, 그는 종교를 시작하려는 의도가 없었고 실제로는 그것을 잘못 설계 한 코드를 보완하기 위해 kludge로 사용하는 것에 반대하는 것만 주장했습니다.

GNU 프로젝트가 printf 패밀리를 g ++ 확장에 추가하면 아주 좋을 것입니다.


1

크기가 중요한 경우 Printf는 실제로 사용하기에 좋습니다. 메모리가 문제가있는 프로그램을 실행하는 경우 printf는 실제로 매우 우수하고 평가자 솔루션입니다. Cout은 기본적으로 문자열을위한 공간을 만들기 위해 비트를 이동시키는 반면 printf는 일종의 매개 변수를 취하여 화면에 인쇄합니다. 간단한 hello world 프로그램을 컴파일하는 경우 printf는 cout과 달리 60,000 비트 미만으로 컴파일 할 수 있으며 컴파일하는 데 백만 비트가 필요합니다.

상황에 따라 id는 사용하기가 훨씬 편리하기 때문에 cout을 사용하는 것이 좋습니다. 그러나 printf가 알아야 할 좋은 점이라고 주장합니다.


1

printf가변 개수의 인수를 허용합니다. 이것들은 POD (Plain Old Data) 유형 만 가질 수 있습니다. printf컴파일러는 형식이 맞다고 가정하기 때문에 POD 이외의 것을 전달하여 컴파일 만하는 코드입니다 . %s는 각각의 인수가에 대한 포인터 여야 함을 의미합니다 char. 귀하의 경우에는 std::string아닙니다 const char*. printf인수 유형이 손실되어 format 매개 변수에서 복원되어야하므로이를 알 수 없습니다. 해당 std::string인수를 const char*결과 포인터 로 돌리면 원하는 C 문자열 대신 관련이없는 메모리 영역을 가리 킵니다. 이런 이유로 코드는 횡설수설로 인쇄합니다.

동안 printf이다 서식있는 텍스트를 인쇄 할 탁월한 선택이 (당신이 패딩을하려는 경우 특히), 당신은 컴파일러 경고를 설정하지 않은 경우, 그것은 위험 할 수 있습니다. 이와 같은 실수는 쉽게 피할 수 있기 때문에 항상 경고를 활성화하십시오 . 가족이 같은 작업을 훨씬 빠르고 예쁘게 수행 할 수 std::cout있다면 서투른 메커니즘 을 사용할 이유가 없습니다 printf. 모든 경고를 활성화했는지 확인하십시오 ( -Wall -Wextra). 사용자 정의 printf구현 __attribute__사용하는 경우 컴파일러가 제공된 매개 변수와 형식 문자열을 검사 할 수있는 메커니즘으로 선언해야합니다 .

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