ページ 11

string型を含む構造体のリストをつくろうとしてるんだと思います

Posted: 2014年10月18日(土) 15:31
by xoke
こんにちは、よろしければどなたかどうしてこうなるのかご教授ください。

コード:

#include <iostream>
#include <fstream>
#include <string>
#include <list>
using namespace std;

// ノード構造体
struct Node
{
  string name;
  int number;
};

int main()
{

  ifstream ifs( "data3.txt" ); // data.txt 読み込み
  list< Node > gNodeList;
  list< Node >::iterator p;
  int gNodeNum = 1;
  string str;

  while( getline( ifs, str ) )
    {
      Node tmp;
      int t;

      tmp.number = gNodeNum;
      
      sscanf( str.c_str(), "%s %d", tmp.name.c_str() , &t );
      cout << tmp.name.c_str() << " " << tmp.number << endl;
      gNodeList.push_back( tmp );
      gNodeNum++;
    }

  Node* pNode;
  

  for( p = gNodeList.begin(); p != gNodeList.end(); ++p)
    {
      cout << "No." <<  p->number << " " << p->name.c_str() << endl;
    }
  
  return 0;
}

このようなコード書いて

data.txt

コード:

hokkaidou 0
akita 0
okinawa 0
というようなファイルを読み込んでgNodeListに格納するというようなのをイメージして書いて見たのですが、
実行すると、

実行結果

コード:

hokkaidou 0
akita 0
okinawa 0
No.1 okinawa
No.2 okinawa
No.3 okinawa
のように、whileループ内でNode型構造体変数tmpに読み込むまではいいんですが、
それをgNodeListに追加するとメンバのstringの値がすべて同じになってしまいます。
こうなってしまう原因を知りたいです。
どうかよろしくお願いします。

Re: string型を含む構造体のリストをつくろうとしてるんだと思います

Posted: 2014年10月18日(土) 16:28
by h2so5
stringの値がすべて同じになるのはtmp.name.c_str()のアドレスが共有されており、同じ領域に何度も上書きして書き込んでいるからです。
下のコードを実行してみてください。

コード:

#include <iostream>
#include <string>
using namespace std;

int main() {
	for (int i = 0; i < 5; ++i) {
		string s;
		cout << (void*)s.c_str() << endl;
	}
}
異なるstringのインスタンスでもすべて同じアドレスが表示されているはずです。
コンパイラの中にはstd::stringにCopy-On-Writeを適用するものがあり、空のstringで同じアドレスを共有しています。
(C++11ではCopy-On-Writeは禁止されていますがgccでは未だに修正されていないようです)

Copy-On-Writeが無かったとしてもこのコードはstringのcapacityを考慮していないのでバッファオーバーフローの危険性があります。


正常に動作するコードはこうなります。
(要 #include <sstream>)

コード:

while( getline( ifs, str ) )
{
      Node tmp;
      int t;
 
      tmp.number = gNodeNum;
      stringstream(str) >> tmp.name >> t;
      
      cout << tmp.name << " " << tmp.number << endl;
      gNodeList.push_back( tmp );
      gNodeNum++;
}

Re: string型を含む構造体のリストをつくろうとしてるんだと思います

Posted: 2014年10月18日(土) 16:58
by xoke
ご回答ありがとうございます。

前半の説明でどうしてこうなってしまったのか理解できたと思います。
ありがとうございます。

後半で示してくださったように修正して実行してみたのですが、

コード:

tmp.cpp: In function 'int main()':
tmp.cpp:31:25: error: no match for 'operator>>' (operand types are 'std::stringstream {aka std::basic_stringstream<char>}' and 'std::string {aka std::basic_string<char>}')
       stringstream(str) >> tmp.name >> t;
                                 ^
 ...
このようなエラーが表示されてコンパイルできませんでした。
自分でも少し解決は試みたのですがerrorの意味はよくわからなかったです。
このエラーについても教えていただけると助かります…

Re: string型を含む構造体のリストをつくろうとしてるんだと思います

Posted: 2014年10月18日(土) 17:13
by h2so5
すみません、そのコードはC++11でないと動かないです。
これを試してみてください。

コード:

while( getline( ifs, str ) )
{
      Node tmp;
      int t;
 
      tmp.number = gNodeNum;
      stringstream ss(str);
      ss >> tmp.name >> t;
      
      cout << tmp.name << " " << tmp.number << endl;
      gNodeList.push_back( tmp );
      gNodeNum++;
}

Re: string型を含む構造体のリストをつくろうとしてるんだと思います

Posted: 2014年10月18日(土) 17:28
by xoke
考えていた通りの動きになりました!

本当にありがとうございました。