なぜSTLのstringのサイズが0なのに、printfできちゃうの?

このQ&Aのポイント
  • stringの初期化時に文字列長を確保してしまうことはできない
  • strTmpのサイズが0にもかかわらず、printfで期待通りの結果が出力される理由は、printfがヌル文字まで出力するため
  • サンプルコードを用いて問題が発生する原因と解決策を説明
回答を見る
  • ベストアンサー

STLのstringのサイズが0なのに、printfできちゃうのはなぜ

STLのstringのサイズが0なのに、printfできちゃうのはなぜ? たとえば、以下のような小文字変換処理を考えます。 std::string str = "ABC"; std::string strTmp = ""; std::transform( str.begin(), str.end(), strTmp.begin(), ptr_fun(::tolower) ); この時、strTmpには"abc"が入っていると思っていたのですが、 strTmp.compare("abc") が0になりませんでした。strTmp.size()が0になっており、どうも空文字""と認識されているようです。 ですが、 printf( strTmp.c_str() ); とすると、"abc"と出力されます。printfで上記のようにデバッグしていたので、なかなかミスが発見できなかったのですが、strTmpの初期化で""を入れているために、compareがうまく動かなかったようです。 std::string strTmp = ""; のところを std::string strTmp = str ; と直すと、compareが期待通りに動きました。 長くなってすみませんが、ここからが質問です。 1.stringの初期化時に確保した文字列長は、その後変更できないのでしょうか?今まで、普通に""で初期化した後、別の文字列を代入したりしていたような気がするのですが…。 2.strTmpのサイズが0にもかかわらず、printfで(一見)期待通りの結果が出力されたのは、なぜでしょうか?printfは、ヌル文字までを出力する仕様みたいですが、そもそもstringのsize()も、ヌル文字までの長さを返すのではなかったでしょうか。 全ソースは、以下の通りです。 #include <string> #include <algorithm> #include <functional> #include <ctype.h> #include <stdio.h> int main() {  std::string str = "ABC";  std::string strTmp = ""; // strにすると動く  std::transform( str.begin(), str.end(), strTmp.begin(), std::ptr_fun(::tolower) );  printf( "compare = %d\n", strTmp.compare("abc") );  printf( "size = %d\n", (int)strTmp.size() );  printf( "strTmp = %s\n", strTmp.c_str() );  return 0; }

  • aneja
  • お礼率93% (379/405)

質問者が選んだベストアンサー

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.1

ざっと見た感じですが、strTempの割り付けられている領域を超えて書き込みを行っているようです。 つまり、未定義の動作なので、たまたま今回のような結果になったと考えるべきです。 > std::transform( str.begin(), str.end(), strTmp.begin(), ptr_fun(::tolower) ); この部分は、 std::transform( str.begin(), str.end(), std::back_inserter(strTmp), ptr_fun(::tolower) ); では? > 1.stringの初期化時に確保した文字列長は、その後変更できないのでしょうか?今まで、普通に""で初期化した後、別の文字列を代入したりしていたような気がするのですが…。 変更できます。 代入やassignでもできますし、push_back、append、resizeなどでも変更できます。 > 2.strTmpのサイズが0にもかかわらず、printfで(一見)期待通りの結果が出力されたのは、なぜでしょうか?printfは、ヌル文字までを出力する仕様みたいですが、そもそもstringのsize()も、ヌル文字までの長さを返すのではなかったでしょうか。 std::stringは、ナル文字までを文字列とするのではなく、別の方法で長さの管理をしているからです。 今回は、正規の方法ではなく、強引にメモリに書き込んでいますので、内部の状態が破壊されているのです。

aneja
質問者

お礼

お礼が遅くなり、申し訳ありません。早速のご回答、どうもありがとうございました。恥ずかしながら、back_inserterというのがあるのは知りませんでした。挿入イテレータというのですね。stringの長さ管理は\0とは直接関係ないのですね。勉強になりました。

その他の回答 (2)

回答No.3

間違ったコードの結果に疑問が生じるのはアタリマエ。 transformするからには出力先には入力と同じかそれ以上のサイズの領域が 確保されていなければなりません。なので正しくは: std::string str = "ABC"; std::string strTmp = ""; strTmp.resize(str.size()); // 領域確保 std::transform( str.begin(), str.end(), strTmp.begin(), std::ptr_fun(::tolower) );

aneja
質問者

お礼

お礼が遅くなり、すみません。ご回答どうもありがとうございました。ご指摘の通りで、納得しました。

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.2

1. std::stringは可変長文字列ですから、stringオブジェクト自体への変更があればそれは反映されます。 2. STLの実装次第です。但し、結果から逆に見ると「文字列長」は別途メンバ変数で持っていてstringオブジェクト自身への演算結果でのみ変更される、という状況に見えます。 で、この前提においてstd::transform()の結果がどうなるかというと…… strTmp.begin()はもちろん内部バッファへのイテレータを返しますね。見掛け上はほぼchar *と変わりません。 となると、ここで「内部バッファにだけ変更がありサイズに適用されない」という状況が発生します。 これでうまく動作しているのは、おそらく文字列長メンバの値とは関係なく一定量のメモリを文字列用に持っているんじゃないかと。

aneja
質問者

お礼

早速のご回答、どうもありがとうございました。お礼が遅くなり、申し訳ありません。元々不正な処理をしているので、おっしゃるとおり、例外で落ちたりしなかったのは、たまたまだったのですね。すっきりしました。

関連するQ&A

  • for文

    以下のプログラムのforの条件文がなぜこれで動くのかよくわからないので 教えていただけないでしょうか? #include<stdio.h> int main(void) { int i; char str[] = "ABC"; char *ptr = "123"; for(i=0;str[i];i++) putchar(str[i]); putchar('\n'); for(i=0;ptr[i];i++) putchar(ptr[i]); putchar('\n'); printf("str = \"%s\"\n",str); printf("ptr = \"%s\"\n",ptr); return(0); }

  • printfがなくても文字が現れるのはなぜ!?

    #include <stdio.h> #include <string.h> int main() { int hensuu = 0; int *p; p = &hensuu; printf("p=%p &hensuu=%p\n", p, &hensuu); printf("*p=%d\n", *p); *p = 100; printf("*p=%d\n", *p); printf("hensuu=%d\n", hensuu); }  以下、出力結果です。    p=0xff930a1c &hensuu=0xff930a1c *p=0 *p=100 hensuu=100  以上が結果ですが!   *p=100 は*p = 100;で終わっていますが!出力結果として文字が出てきたいるのはなぜですか!  宜しくお願いします。

  • ポインタについて

    #include<stdio.h> int main(void) { char str[10]; char *ptr = str; printf("文字列を入力してください。\n"); scanf("%s",ptr); printf("文字列は%sです。",str); return 0; } 上記のプログラムのscanf("%s",ptr);の ptrに&をつけるとなぜ先頭の4文字は入力しても 表示されなくなってしまうのでしょうか? よろしくお願いします。

  • printf文で"という文字を出力したい

    #include <stdio.h> #include <stdlib.h> #include <string.h> main() { printf("a"b"c\n"); } 標準出力でa"b"cと出力したいのですが、エラーが出てしまいます。 printf文で"を出力することは可能なのでしょうか。 教えてください、お願いします。

  • c++のstd::stringについて

    VC++2008でフォームアプリケーションを作成しています。 シリアルポートから受け取った文字列の一部を抜き出して処理をするため,VBではmid関数に相当するような機能として,std::stringを使用しようとしています。 しかし, std::string str("ABC" ,1,2); とした場合は『BC』が問題なく返ってきましたが, std::string str(recieveddata ,1,2); のように,文字列の部分を変数にしたら,ビルドエラーになってしまいます。(ポインタ?を理解する必要があるのでしょうか?) どのようにすれば,VBのmid相当の機能を実現できるでしょうか?

  • ファイル操作

    今C言語のファイル操作を勉強中です。 作ったソースが問題の指示通りのことをやれているか分からないので、違うことを書いていたらご指摘お願いします。 問題は「新規に作成したファイルに、キーボードから取り込んだ5つの文字列を順次書き込むプログラムを作成せよ。更に、そのファイルからデータを文字列単位で読み出して、画面に表示させる処理を付け加えよ。」 ソースは以下です ----------------------------------------------- #include<stdio.h> void main(void) { FILE *str_ptr; char string[30]; int i; str_ptr = fopen("outfile", "w"); printf("5個の文字列入力してください。\n"); for(i=0 ; i<5 ; i++){ scanf("%s", string); fputs(string, str_ptr); } fclose(str_ptr); str_ptr = fopen("outfile", "r"); fgets(string, 30, str_ptr); printf("%s ", string); fclose(str_ptr); } ------------------------------------------------- 実行結果は、例えば「"suzuki","katou","sugiura","sasaki","kawai"」を入力したとすると suzukikatousugiurasasakikawai と全部つながってしまいます。 「ファイルからデータを文字列単位で読み出して」という言葉が何か引っかかります。 これで大丈夫なのでしょうか?

  • STL string::findで見つからなかった時

    STL string::findで見つからなかった時の 書式を教えて下さい。 <-----ソース start-------> #define UNCODE #define _UNCODE #pragma warning( push,3 ) #pragma warning( disable : 4786 ) // 識別子が '255' 文字に切り捨 #include <iostream> #include <string> #include <vector> #pragma warning( pop ) #pragma warning( disable : 4514 ) // 参照されていないインライン関数は削除 #pragma warning( disable : 4786 ) // 識別子が '255' 文字に切り捨 int wmain(int iArgC, wchar_t* ArgV[], wchar_t* EnvP[]) {  setlocale( LC_ALL, "Japanese" );  std::vector<std::wstring> m_sEnv;  std::vector<std::wstring> m_sArg;  unsigned long lLoop;  unsigned long lPos;  for (lLoop=0; lLoop<(unsigned long)iArgC; lLoop++) {   m_sArg.push_back(ArgV[lLoop]);  }  for (lLoop=0;;lLoop++) {   if (EnvP[lLoop] == NULL) break;   m_sEnv.push_back(EnvP[lLoop]);  }  for (lLoop=0;lLoop<m_sEnv.size();lLoop++) {   if ((lPos = m_sEnv.at(lLoop).find(L"jdk1")) != npos) { // *1    std::wcout << m_sEnv.at(lLoop) << std::endl;   }  }  return 0; } <-----ソース end-------> *1 で error C2065: 'npos' : 定義されていない識別子です。 「std::npos」も試したけどだめだった。 >>以下MSDNより >>basic_string::npos >>static const size_type npos = -1; >>この定数は、size_type 型として表現できる最大の値です。 >>max_size() よりも大きいことが保証されるため、 >>非常に大きな値または特殊なコードとして使用できます。

  • boost::formatの値をstring型にコピーしたい

    boost::formatの値をstring型にコピーしたいのですが、うまくいきません。boost自体初めてで以下のサイトからダウンロードし、展開後VS2005のVCのインクルードフォルダーにboostフォルダーをまるまるコピーしただけですが・・・ http://sourceforge.net/project/showfiles.php?group_id=7586 boost 1.34.1 #include <iostream> #include <boost/format.hpp> using namespace std; using boost::format; void main(){ double x = 1.234; string str("abc"); //cout << format("%10.3f, [%16s]") % x % str << endl; // サンプルはこうでした。 // いったんstring型に入れて表示させたい。 string y; y = boost::format("%10.3f, [%16s]") % x % str; // エラー箇所 std::cout << y << std::endl; }

  • [C]char型のダブルポインタ

    粗雑で申し訳ありませんが、 以下のソースをコンパイルできましたが、 うまく実行できません。 自分なりに間違いがないと思うのですが、 間違い等をご指摘頂ければ助かります。 #include <stdio.h> void func(char **ptr) ptr[][10] か (*ptr)[] なら通る *ptr[] は通らない { printf("----- func -----"); printf("%s\n", *ptr); printf("%c\n", **ptr); putchar('\n'); } int main(void) { char str[5][10] = {"AAAAA", *str[] にすると func で **ptr で通る "BBBBB", "CCCCC", "DDDDD", "EEEEE", }; printf("----- main -----"); printf("%s\n", *str); printf("%c\n", **str); putchar('\n'); func(str); return (0); } 実行結果 ----- main ----- AAAAA A ----- func ----- Bus error (core dump) 関数への受け渡しで、型が違うというお叱りを受けますが、 コンパイルはできました。 コンパイラはCCです。 ではよろしくお願いします。

  • stringについて

    C++初心者です。 このプログラムで続行するとエラーがでます。どうしたら無事実行することが出来るのでしょうか? #include<stdio.h> #include <iostream> using namespace std; int main(void) { string str("エラー"); cout << str<< endl; } エラー 1>c:\documents and settings\****\デスクトップ\zisyu12\zisyu12\main.cpp(58) : error C2679: 二項演算子 '<<' : 型 'std::string' の右オペランドを扱う演算子が見つかりません (または変換できません)。 . . . 以下省略

専門家に質問してみよう