188

私の理解では、それ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文字の文字列が出力されます。

4

8 に答える 8

283

Cセンス1printfで可変引数を使用するため、タイプセーフではないためコンパイル中です。のオプションはなく、Cスタイルの文字列のみです。期待するものの代わりに何か他のものを使用しても、あなたが望む結果は確実に得られません。これは実際には未定義の動作であるため、何かが発生する可能性があります。printfstd::string

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

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

何らかの理由でCスタイルの文字列を抽出する必要がある場合は、のc_str()メソッドを使用して、 nullで終了std::stringするを取得できます。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以降のすべての主要なコンパイラでサポートされています)を調べてください。ここにその例があります。標準ライブラリにそのように実装されていることを私が知っていることは何もありませんが、特にBoostにあるかもしれませんboost::format


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

于 2012-06-02T21:09:02.503 に答える
44

使用しないでくださいprintf("%s", your_string.c_str());

cout << your_string;代わりに使用してください。短く、シンプルでタイプセーフ。実際、C ++を作成するときは、通常、完全に避けたいと考えています。これはprintf、C ++でほとんど必要とされない、または役立つCからの残り物です。

の代わりに使用する必要がある理由については、多くの理由があります。最も明白ないくつかのサンプルを次に示します。coutprintf

  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プログラマーの間でも、少なくとも99%は、inの意味と、それが#inの意味%#xとどのように異なるかを確認するために物事を調べる必要があると思います(もちろん、まったく異なる意味です)。 )。#%#f
于 2012-06-02T21:36:08.567 に答える
35

myString.c_str()cのような文字列(const char*)をprintfで使用する場合に使用します

ありがとう

于 2012-06-02T21:08:56.180 に答える
11

std :: printfおよびc_str()の例を使用します。

std::printf("Follow this command: %s", myString.c_str());
于 2015-11-26T17:27:18.277 に答える
2

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

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

于 2017-09-18T10:52:03.717 に答える
1

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

私自身について言えば、htmlのテーブル構造にdivで簡単に入力できない場所があるのと同じように、printfにはC++の構文機能で簡単に入力できない場所があると思います。Dykstraが後で後藤について書いたように、彼は宗教を始めるつもりはなく、実際には、不十分に設計されたコードを補うための応急修理としてそれを使用することに反対しているだけでした。

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

于 2015-09-15T20:02:23.483 に答える
1

サイズが重要な場合、Printfは実際に使用するのに非常に適しています。つまり、メモリが問題となるプログラムを実行している場合、printfは実際には非常に優れた評価の低いソリューションです。Coutは基本的にビットをシフトして文字列用のスペースを確保しますが、printfはある種のパラメーターを取り込んで、それを画面に出力します。単純なhelloworldプログラムをコンパイルする場合、printfはcoutとは対照的に60,000ビット未満でコンパイルでき、コンパイルには100万ビット以上かかります。

あなたの状況では、idはcoutを使用することをお勧めします。これは、使用する方がはるかに便利だからです。ただし、printfは知っておくとよいことだと思います。

于 2016-06-17T05:51:09.863 に答える
0

を使用snprinftして、必要な文字数を決定し、適切なサイズのバッファーを割り当てることができます。

int length = std::snprintf(nullptr, 0, "There can only be %i\n", 1 );
char* str = new char[length+1]; // one more character for null terminator
std::snprintf( str, length + 1, "There can only be %i\n", 1 );
std::string cppstr( str );
delete[] str;

これは、cppreference.comの例を少し変更したものです。

于 2022-02-03T15:47:19.713 に答える