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

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
xoke

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

#1

投稿記事 by xoke » 9年前

こんにちは、よろしければどなたかどうしてこうなるのかご教授ください。

コード:

#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の値がすべて同じになってしまいます。
こうなってしまう原因を知りたいです。
どうかよろしくお願いします。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

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

#2

投稿記事 by h2so5 » 9年前

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

xoke

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

#3

投稿記事 by xoke » 9年前

ご回答ありがとうございます。

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

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

コード:

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の意味はよくわからなかったです。
このエラーについても教えていただけると助かります…

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

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

#4

投稿記事 by h2so5 » 9年前

すみません、そのコードは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++;
}

xoke

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

#5

投稿記事 by xoke » 9年前

考えていた通りの動きになりました!

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

閉鎖

“C言語何でも質問掲示板” へ戻る