string型とchar型について

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

string型とchar型について

#1

投稿記事 by kouki » 10年前

文字列についてよく理解できてないので、string型をchar型のポインタで返す関数を作ってみました。
しかし、何回か呼ぶと最後に呼んだ時の変数で初期化されてしまいます。
これを何とか回避したいのですが、方法などはあるのでしょうか。

コード:

const char* test( const string& str )
{
    return str.c_str();
}
    
      const char* str1 = test( "1234"  );
       const char* str2 = test( "5678"  );

〜ここでログ出力〜

ログ
str1 = 5678
str2 = 5678

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: string型とchar型について

#2

投稿記事 by みけCAT » 10年前

引数のデータのコピーを取るといいかもしれません。
► スポイラーを表示
【訂正】std::vectorの再確保後もc_str()が同じになるか自信が無いので、このコードだとダメかもしれません。
下に書き直します。
最後に編集したユーザー みけCAT on 2014年12月23日(火) 14:38 [ 編集 1 回目 ]
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Blue

Re: string型とchar型について

#3

投稿記事 by Blue » 10年前

みけCAT さんが書きました: const char* test( const string& str )
{
static std::vector<string> str_list;
str_list.push_back( str );
return str_list.back().c_str();
}
[/code]
vectorだとキャパシティを超えたときに問題が出そうですね。listのほうがよさそう。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: string型とchar型について

#4

投稿記事 by みけCAT » 10年前

コンストラクタとデストラクタを通るときに情報を出力するようにすると、
何が起きているかわかりやすくなるかもしれません。

コード:

#include <cstdio>
#include <string>
#include <vector>

using std::string;

struct hoge : public string {
	hoge(): string() {
		printf( "hoge %p constructer\n"  , (void*)this  );
	}
	hoge( const char* s ): string( s  ) {
		printf( "hoge %p constructer content=%s (%p)\n"  , (void*)this  , s  , (void*)s  );
	}
	hoge( const hoge& h ): string( h  ) {
		printf( "hoge %p constructer copy from %p\n"  , (void*)this  , (void*)&h  );
	}
	~hoge() {
		printf( "hoge %p destructer\n"  , (void*)this  );
	}
	hoge& operator=( const hoge& h ) {
		printf( "hoge %p set hoge %p\n"  , (void*)this  , (void*)&h  );
		*((string*)this) = h;
		return *this;
	}
	hoge& operator=( const char* s ) {
		printf( "hoge %p set content %s (%p)\n"  , (void*)this  , s  , (void*)s  );
		*((string*)this) = s;
		return *this;
	}
	const char* c_str() const {
		const char* ret = ((string*)this)->c_str();
		printf( "hoge %p c_str() = %p\n"  , (void*)this  , (void*)ret  );
		return ret;
	}
};

const char* test( const hoge& str )
{
	return str.c_str();
}

int main( void )
{
	const char* str1 = test( "1234"  );
	const char* str2 = test( "5678"  );

	printf( "str1 = %s (%p)\n" , str1  , (void*)str1  );
	printf( "str2 = %s (%p)\n" , str2  , (void*)str2  );

	return 0;
}
【修正】std::vectorを用いたtest関数(No: 2と同じ)を削除
最後に編集したユーザー みけCAT on 2014年12月23日(火) 14:52 [ 編集 1 回目 ]
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

kouki

Re: string型とchar型について

#5

投稿記事 by kouki » 10年前

皆様ありがとうございます!
何回もと書いてしまったのですが、2回以降からこの現象が発生しております。
値は呼び出し先の関数内で保持できれば十分なのですが、よくやり方がわからなくて・・・。
vectorなどを使う方法しかないのでしょうか?

kouki

Re: string型とchar型について

#6

投稿記事 by kouki » 10年前

何度もすみません!
ログを出すと、test関数内では正しい値を保持しております。
これを戻り値で返すと、最後に呼んだものに上書きされてしまいます。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: string型とchar型について

#7

投稿記事 by みけCAT » 10年前

vectorは使うべきではないです。訂正版のコードです。

コード:

#include <cstdio>
#include <string>

using std::string;

const char* test( const string& str )
{
	class str_list_node {
		private:
			string data;
			str_list_node* next;
		public:
			const char* get_str() const
			{
				return data.c_str();
			}
			void set_next ( str_list_node* next_node )
			{
				next = next_node;
			}
			str_list_node(): data(), next( NULL  )
			{
			}
			str_list_node( const string& str): data ( str  ), next( NULL  )
			{
			}
			~str_list_node()
			{
				if ( next != NULL  ) delete next;
			}
	};
	static str_list_node head;
	static str_list_node* next_to_write = &head;

	str_list_node* next_node = new str_list_node( str  );
	next_to_write->set_next ( next_node  );
	next_to_write = next_node;
	return next_node->get_str();
}

int main( void )
{
	const char* str1 = test( "1234"  );
	const char* str2 = test( "5678"  );

	printf( "str1 = %s\n" , str1  );
	printf( "str2 = %s\n" , str2  );

	return 0;
}
ログ出力をつけるとこうなります。
► スポイラーを表示
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Blue

Re: string型とchar型について

#8

投稿記事 by Blue » 10年前

kouki さんが書きました:何度もすみません!
ログを出すと、test関数内では正しい値を保持しております。
これを戻り値で返すと、最後に呼んだものに上書きされてしまいます。
test関数の戻り値は一時オブジェクトであるstd::stringの値を返しているので想定した通りになりません。
イメージ的には

コード:

    const char* str1;
    const char* str2;

    {
        std::string str("1234");
        str1 = str1.c_str();
    }
    {
        std::string str("5678");
        str2 = str2.c_str();  
    }
と同じ様なことです。
寿命が尽きているオブジェクトの値を使うこと自体が間違いです。
(たまたまstr2は正しく出ているだけ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: string型とchar型について

#9

投稿記事 by みけCAT » 10年前

kouki さんが書きました:string型をchar型のポインタで返す関数を作ってみました。
そもそも「string型をchar型のポインタで返す」という操作の定義がよくわからないのですが、
「string型」(そもそもC++のSTLのstd::string型(std::basic_string<char>型)である保証も無い)
のインスタンスのc_str()の戻り値を返せばいいのですね?
kouki さんが書きました:値は呼び出し先の関数内で保持できれば十分なのですが、よくやり方がわからなくて・・・。
vectorなどを使う方法しかないのでしょうか?
「値」とは何ですか?
const char*型の「値」を保持できれば、それが指す先の内容は保証しなくていいのですか?
それとも、返ってきたポインタが指すデータを保持したいのですか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

kouki

Re: string型とchar型について

#10

投稿記事 by kouki » 10年前

サンプルソースまでありがとうございます!
頂いた関数だと問題なく正しい値を取得出来ました。
私の関数の戻り値のやり方はまずかったのですね。

やりたかったのは
string型のc_str()でchar型に変換する部分を関数でということになります。
思いついてやってみたのですが、上手くいかなかったので、
何か書き方が違うんだろうなと思い質問させて頂きました。

文字列についてよく理解できていなくて・・・。
やりたかったことは頂いた関数と同じことなのですが、
あまりにも複雑で理解出来ませんでした。

理解できるように解読させて下さい!
本当にありがとうございました!!!

閉鎖

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