質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

4回答

11197閲覧

文字が出力されない.

aglkjggg

総合スコア769

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/11/28 21:24

###発生している問題・エラーメッセージ
str2が正しく出力されません。

1~3は同じ事を別の書き方でしているだけと考えていましたが、
str2が出力されないことからこの考え間違っていた事がわかりました。
しかし、何故出力されないのかが理解できません。

何故出力されないかの解説または、調べる方法等を教えてください。

###該当のソースコード

C++

1#include <iostream> 2 3int main() 4{ 5 // 1. OK 6 auto str1 = std::string("abc"); 7 std::cout << str1.c_str() << std::endl; 8 9 // 2. NG 10 auto str2 = std::string("abc").c_str(); 11 std::cout << str2 << std::endl; 12 13 // 3. OK 14 std::cout << std::string("abc").c_str() << std::endl; 15 return 0; 16} 17

実行結果

###補足情報(言語/FW/ツール等のバージョンなど)
Windows 10 64bit 1703
Visual Studio 2017
Windows SDK version 10.0.16299.0
プラットフォームツールセット Visual Studio 2017 (v141)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

str2が指している先が一時オブジェクトだからだと考えられます。
std::string("abc").c_str()においてstd::string("abc")オブジェクトは.c_str()によってconst char*を返した後は破棄されます。よってその後にstr2で指された領域にアクセスしてはいけません。
以下のケース22と同様と考えてもよいと思います。

ちなみに最近似た質問が上がっていたので参照ください。
string(5, 'A')で起きていることについて(C++)

C++

1#include <iostream> 2int main() 3{ 4 // 1. OK 5 auto str1 = std::string("abc"); 6 std::cout << str1.c_str() << std::endl; 7 8 // 2. NG 9 auto str2 = std::string("abc").c_str(); 10 std::cout << str2 << std::endl; 11 12 // 22. NG 13 const char* str22; 14 { 15 std::string tmp = std::string("abc"); 16 str22 = tmp.c_str(); 17 } 18 std::cout << str22 << std::endl; 19 20 // 3. OK 21 std::cout << std::string("abc").c_str() << std::endl; 22 return 0; 23}

あ、ideoneでは全部出力されますね。
未定義動作なので何が起きても不思議ではありませんが。

投稿2017/11/29 00:22

編集2017/11/29 00:26
can110

総合スコア38230

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

catsforepaw

2017/11/29 01:12

> 未定義動作なので何が起きても不思議ではありませんが。 完全に処理系依存ですね。 VC++のbasic_stringの実装は、メンバ変数に16バイトのバッファ領域を持っていて、そこに収まる短い文字列に関してはヒープを使わずにパフォーマンスを稼ぐ、ということをしています。そして、デストラクタでそのバッファ領域の先頭に0を書いています。したがって、"abc"のように短い文字列だと、オブジェクトが破棄された直後のc_str()が返したアドレスには0が入っていることになりご質問のような現象が発生します。 16文字以上の長い文字列にすると結果が変わります。その場合、文字列はヒープに格納され、Releaseビルドなら領域解放後も中身は残されるので直前に設定した文字列がそのまま出力されます。Debugビルドだとデバッグコードが領域内を特定値で埋め尽くすので、変な文字列が出力されることになります。 basic_stringもデバッグビルド時は内部バッファを特定値で埋め尽くすような実装にしていれば、ご質問のようなバグを発見しやすいのですけどね。
can110

2017/11/29 01:32

>そのバッファ領域の先頭に0を書いています。 なるほど。空文字列として出力されるのですね。 >デバッグビルド時は内部バッファを特定値で埋め尽くすような実装にしていれば 16バイトのバッファ領域があるなら「you are fool!!!」とかでもよいかも(w
aglkjggg

2017/11/29 02:28

can110さん、catsforepawさん ご回答ありがとうございます。 特にcatsforepawさん、非常に詳細な情報ありがとうございます。 VisualStudioのデバッグとウォッチを利用するとおっしゃる通りの動作が確認できました。 ありがとうございます、深い理解を得ることが出来ました。 https://i.imgur.com/zhHmNa2.png
Chironian

2017/11/29 02:32

横から失礼します。 この構文なら、str2はC言語文字列からコンストラクトされる筈です。C言語文字列用のコンストラクタはchar const *で受け取るので当然一時オブジェクトを渡せる筈。 でも、実際には可笑しくなるし。最適化のための特別仕様があるのかも? 難易度高そうな気がします。 https://wandbox.org/permlink/gtvipHottCewLZ0Y
catsforepaw

2017/11/29 02:45

Chironianさん 何か勘違いをされているのでしょうか? > auto str2 = std::string("abc").c_str(); この場合、str2は`const char *`で定義されるので、can110さんの書かれたコード例の「str22」とほぼ等価です。不正なコードであることは一目瞭然だと思うのですが……。
can110

2017/11/29 02:56

私はautoの型推論について以下のように素直に考えました。 c_str()によりconst char*が返されるのですからstr2は「const char*」型だと。 たしかにc_str()によってナル文字終端のC言語文字列が生成されると考えられます。 そして、その戻り値の型const char*を引数とする「何か」のコンストラクタにより「何か」のオブジェクトが生成できるかもしれません。 しかしその「何か」は「std::string」とは限らず推論できないと考えます。
catsforepaw

2017/11/29 04:03

> c_str()によりconst char*が返されるのですからstr2は「const char*」型だと。 えーと、そういう話? それならそれで合っていると思います。autoの型推論は関数テンプレートの引数と同じはずなので、右辺値がconst char*なら、autoの型もconst char*になります。なってくれないと困ります。
Chironian

2017/11/29 04:10 編集

あああ、そっかそうでした。単純にstr2をstd::string型を思い込んでました。お二方、ありがとう。
can110

2017/11/29 04:19

autoについての理解が足りないので 「str2 = std::string("abc").c_str()」でもしかして何か別の型のオブジェクトが生成される可能性もあるのかな?と新たな疑問が生じていました。が、それはないですね。 catsforepawさん、Chironianさんコメントありがとうございました。
guest

0

こんにちは。

str2はもう破壊されたメモリーを指しているので何も出力しないんです。
もう詳しく説明すると、

auto str2 = std::string("abc").c_str();

まず、str2は const char *になります。
そして、std::string("abc")用一時オブジェクトが生成されて、
c_str()で、その一時オブジェクトの内側にあるpointerがstr2に代入されます。
最後にstd::string("abc")用一時オブジェクトは破壊されます。
なのでstr2はinvalid状態になり何が出力されるか分かりません。

(蛇の足)
str1は std::string タイプになります。
なので一時オブジェクトがコピーされますのでオッケーです。
3の場合は一時オブジェクトが破壊される前に出力されます。なのでそれもオッケーです。

投稿2017/11/29 00:37

編集2017/11/29 01:01
gazette2

総合スコア179

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2017/11/29 03:17

ご回答ありがとうございます。 処理順序に従って書いていただき分かりやすかったです。
guest

0

GCC/G++でコンパイルするときに
-std=c++11 を付けなければ当然ながらコンパイルエラーになります。

autoというのは型推論ですので。

投稿2017/11/28 23:51

naohiro19_

総合スコア178

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2017/11/28 23:55 編集

回答ありがとうございます。 しかし、今回質問させて頂いた件はstr2が正しく出力されない原因についてです。 コンパイルエラーやautoについては問題が発生していません。 また、開発環境は補足情報で明記してる通りWindowsでVisualStudioを利用しています。 gcc/g++は利用していません。 str2が正しく出力されない原因について何かわかりましたら教えて頂ければと思います。
guest

0

ワーニングは出るけど実行出来ますね・・・環境の問題か?

C++

1kondo@usr~/test >c++ tst01.cpp 2tst01.cpp:6:5: warning: 'auto' type specifier is a C++11 extension 3 [-Wc++11-extensions] 4 auto str1 = std::string("abc"); 5 ^ 6tst01.cpp:6:5: warning: 'auto' type specifier is incompatible with C++98 7 [-Wc++98-compat] 8 auto str1 = std::string("abc"); 9 ^~~~ 10tst01.cpp:10:5: warning: 'auto' type specifier is a C++11 extension 11 [-Wc++11-extensions] 12 auto str2 = std::string("abc").c_str(); 13 ^ 14tst01.cpp:10:5: warning: 'auto' type specifier is incompatible with C++98 15 [-Wc++98-compat] 16 auto str2 = std::string("abc").c_str(); 17 ^~~~ 184 warnings generated. 19[実行結果] 20kondo@usr~/test >./a.out 21abc 22abc 23abc 24kondo@usr~/test >cat tst01.cpp 25#include <iostream> 26 27int main() 28{ 29 // 1. OK 30 auto str1 = std::string("abc"); 31 std::cout << str1.c_str() << std::endl; 32 33 // 2. NG 34 auto str2 = std::string("abc").c_str(); 35 std::cout << str2 << std::endl; 36 37 // 3. OK 38 std::cout << std::string("abc").c_str() << std::endl; 39 return 0; 40}

「追記」
str2に何が入るかデバッガで追いかけるのが得策かとd^^
あ!、stringをインクルードするか、c_str()呼ぶかしないとcoutには出せません。

投稿2017/11/28 21:48

編集2017/11/28 21:55
cateye

総合スコア6851

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2017/11/28 22:10

回答ありがとうございます。 WSLで確認した所動きました… VC++側の何らかの問題の気がしてきました>< https://i.imgur.com/QgysesS.png gcc version 5.4.0 20160609
catsforepaw

2017/11/29 01:22

> VC++側の何らかの問題の気がしてきました いいえ、can110さんの回答が正しいです。そちらを読んでください。gccで実行できているように見えるとしたら、むしろその方が問題なのです。
aglkjggg

2017/11/29 01:59

can110さんの回答がまだ来てない状態で、g++でコンパイルした物だと動作したので「VC++側の問題」かと思いました><
catsforepaw

2017/11/29 02:01

あぁ、そうでしたか。コメントの前後関係までは見ていませんでした。失礼しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問