web-dev-qa-db-ja.com

printfとstd :: stringの組み合わせは?

stringstd名前空間のメンバーであると私は理解しています。では、なぜ次のようになるのでしょうか。

#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;
}

enter image description here

プログラムが実行されるたびに、myStringは、上の出力のように、一見ランダムな3文字の文字列を出力します。

136
TheDarkIn1978

printfはCの意味で可変引数を使用するため、タイプセーフではないため、コンパイル中です。1printfにはstd::stringのオプションはなく、Cスタイルの文字列のみがあります。それが期待するものの代わりに他のものを使用することは間違いなくあなたにあなたが望む結果を与えません。実際には未定義の動作であるため、何でも起こります。

これを修正する最も簡単な方法は、C++を使用しているため、通常std::coutで印刷することです。これは、std::stringが演算子のオーバーロードを通じてサポートしているためです。

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

何らかの理由でCスタイルの文字列を抽出する必要がある場合は、std::stringc_str()メソッドを使用して、NULLで終了するconst char *を取得できます。あなたの例を使用して:

#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に似ているがタイプセーフな関数が必要な場合は、変数テンプレート(C++ 11、MSVC12以降のすべての主要なコンパイラでサポート)を調べてください。 here の例があります。標準ライブラリにそのように実装されていることは私が知っていることはありませんが、Boost、特に boost::format にあるかもしれません。


[1]:これは、任意の数の引数を渡すことができることを意味しますが、関数は、それらの引数の数とタイプを伝えるためにあなたに依存しています。 printfの場合、%dのようなエンコードされた型情報を持つ文字列はintを意味します。型や数値についてうそをつくと、関数には標準的な認識方法がありませんが、一部のコンパイラーはうそをつくときにチェックして警告を出すことができます。

214
chris

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として印刷されるでしょう(そしてもちろん他にもたくさんあります)。 printfを使用すると、正確に1つの結果が得られます。123456.78。それは一貫していますが、どこにいても一貫して間違っています。基本的にこれを回避する唯一の方法は、書式設定を個別に行い、結果を文字列としてprintfに渡すことです。printf自体は単純にしません仕事は正しく。

  4. それらはかなりコンパクトですが、printfフォーマット文字列は全く読めないことがあります。 printfを実質的に毎日使用しているCプログラマーの間でも、#%#xが何を意味するのか、そしてそれが#%#fが何を意味するのかを確かめるために少なくとも99%調べなければならないでしょうそれらはまったく異なるものを意味します。
40
Jerry Coffin

printfでcのような文字列(const char *)を使用したい場合は、myString.c_str()を使用してください。

24

std :: printf およびc_str()の例を使用してください。

std::printf("Follow this command: %s", myString.c_str());
6
Adel Ben Hamadi

サイズが問題であれば、printfは実際にはかなり使用に適しています。あなたがメモリが問題となっているプログラムを走らせているならば、printfは実際には非常に優れていて評価が十分ではありません。 Coutは文字列のためのスペースを空けるために基本的にビットをシフトしますが、printfはある種のパラメータを取り込んでそれを画面に表示します。単純なhello worldプログラムをコンパイルする場合、printfはcoutとは対照的に60,000ビット未満でコンパイルできます。コンパイルには100万ビット以上かかります。

あなたの状況では、idはcoutの使用をお勧めします。それは単に使用するほうがはるかに便利だからです。しかし、私はprintfが知っておくと良いことだと主張するでしょう。

2
howard howard

printfは、可変個の引数を受け入れます。それらはPlain Old Data(POD)タイプだけを持つことができます。 POD以外のものをprintfに渡すコードは、ユーザーが正しい形式であると見なすため、コンパイルのみを行います。 %sは、それぞれの引数がcharへのポインタであると想定されていることを意味します。あなたの場合それはstd::stringではなくconst char*です。引数の型が失われて、formatパラメータから復元されることになっているため、printfはそれを認識していません。そのstd::string引数をconst char*に変えるとき、結果のポインタはあなたの望んだC文字列の代わりに無関係なメモリの領域を指すでしょう。そのため、あなたのコードはちんぷんかんぷんと印刷します。

printfフォーマットされたテキスト を印刷するための優れた選択ですが(特にパディングを使用したい場合)、コンパイラの警告を有効にしていないと危険です。 このような間違いは簡単に回避できるため、常に警告を有効にしてください。 printfファミリが同じタスクをはるかに速くてきれいに実行できる場合は、不器用なstd::coutメカニズムを使用する理由はありません。すべての警告(-Wall -Wextra)が有効になっていることを確認してください。独自のカスタムprintf実装を使用する場合は、 を指定したパラメーターに対してフォーマット文字列をチェックする ことを可能にする__attribute__メカニズムを使用して宣言する必要があります。

1
Hyena

主な理由はおそらく、C++文字列が0バイトで終了する一連の文字のアドレスだけでなく、現在の長さの値を含む構造体であることです。 Printfとその親戚は、構造体ではなくそのようなシーケンスを見つけることを期待しているので、C++文字列と混同されます。

私自身といえば、htmlのテーブル構造がdivで簡単に埋めることができない場所があるように、printfにはC++構文機能で簡単に埋めることができない場所があると思います。 Dykstraが後藤について後で書いたように、彼は宗教を始めるつもりはなかった、そして実際にはよく設計されていないコードを埋め合わせるための問題としてそれを使用することに対して反対しているだけだった。

GNUプロジェクトがprintfファミリーを彼らのg ++​​拡張に追加することができれば、それは非常に素晴らしいことです。

1
MMacD