• ベストアンサー

char[]とchar*

#include<iostream.h> main() { char str1[] = "AB"; char *str2 = "ab"; *(str1+1) = 'C'; *(str2+1) = 'c'; printf("%s\n", str1); printf("%s\n", str2); } このソースの *(str2+1) = 'c'; の所はC++では間違った処理ですか? []かnewなどの変数なら書き換えてよいのは分かりますが、str2はこれでよいのか教えて下さい。

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

  • ベストアンサー
  • suseimei
  • ベストアンサー率35% (17/48)
回答No.2

参考のために、MSの無料ツールVC++ 2005 Expression Editionが生成するアセンブラーコードを紹介させていただきます。 5: char str1[] = "AB"; 004113AE 66 A1 04 57 41 00 mov ax,word ptr [string "AB" (415704h)] 004113B4 66 89 45 F8 mov word ptr [str1],ax 004113B8 8A 0D 06 57 41 00 mov cl,byte ptr ds:[415706h] 004113BE 88 4D FA mov byte ptr [ebp-6],cl 6: char* str2 = "ab"; 004113C1 C7 45 EC 00 57 41 00 mov dword ptr [str2],offset string "ab" (415700h) 7: *(str1+1) = 'C'; 004113C8 C6 45 F9 43 mov byte ptr [ebp-7],43h 8: *(str2+1) = 'c'; 004113CC 8B 45 EC mov eax,dword ptr [str2] 004113CF C6 40 01 63 mov byte ptr [eax+1],63h 使用中の処理系の結果と比較してみてください。速度を重視する場合やセキュリティを配慮する必要がある場合など、いろいろ考えることがあるようです。 参考になれば幸いです。

letrmf
質問者

お礼

004113CF C6 40 01 63 mov byte ptr [eax+1],63h のところで+1を見落としていました。 eax+1が指すメモリ、つまり"ab"のアドレス+1に63h('c')を入れるですか?

letrmf
質問者

補足

アセンブラ自体が分かりません。 ソースの5行目がtest.objに 66 A1 04 57 41 00 66 89 45 F8 8A 0D 06 57 41 00 88 4D FA というデータで含まれることになるということですか? 004113CC 8B 45 EC mov eax,dword ptr [str2]は、 "ab"のアドレスをeaxに入れる。 004113CF C6 40 01 63 mov byte ptr [eax+1],63h は、 eaxが指すメモリ、つまり"ab"のアドレスに63h('c')を入れるですか? 便利なソフトですね。でもこのアセンブラを見ると、やってはいけないことをやっているということが分かるんですか?

その他の回答 (9)

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

この場であまり議論すると、言語規格の問題はクリアできても、サイトの利用規約に抵触しそうですが... JIS X3014の1.3.12 未定義の動作(undefined behavior)では、参考として、 翻訳段階又はプログラムの実行段階で, その環境に沿っての動作をする(診断情報を出してもよいし, 出さなくてもよい。)。ただし, その動作の様子については, 文書を用意して説明しておく。 とあります。 また、 この規格で動作を明示的に与えていない場合も, 未定義の動作とする。 ともあるように、処理系の独自拡張機能のほとんどは(規格で動作が明示的に与えられていないので)未定義の動作になりますが、処理系が文書を用意して動作を説明しているから、移植性にさえ目をつぶれば安心して使えるのです。 実際、文字列リテラルの領域への書き込みは、共通の拡張として多くの処理系がサポートしています。これについても、移植性にさえ目をつぶれば、使用することに特に問題はないはずです。

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.9

補足: 規格定義->処理系定義->ユーザ定義の流れがあるとして、 undefined behavior だと、規格の時点で結果が未定義だと規定してる。 unspecified behavior だと、規格はその件に関しては何も規定しないと宣言しているので、処理系が何かを規定していれれば、ユーザからみるとその規定が有効、 という感じでしょうか。

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.8

undefined behaviorは処理系を拘束しないので、C/C++ のコードとしてみた場合(実際の挙動とは無関係に、規格/仕様としてみた場合)、常に不適格と考えるべき類のものです。 unspecified behavior であれば、処理系によって適切に定義されているならば、処理系依存ですがその環境では有効と考えられます。 ※implementation-defined は、規格により各処理系が定義することを求めているので、規格準拠の処理系では何らかの定義があるはずです。(内容自体は処理系依存が普通です) ※C++ の場合、規格書の 1.3.12, 1.3.13あたりに 上記behavior の定義があります。 今回の記述は undefined behavior に該当しますので、処理系が何らかの定義を行っているなら確かに動作は不測とはいえないものの、言語的な保証がないことには代わりがありません。

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

文字列リテラルの領域への書き込みが未定義の動作であることは他の方々が書いておられるとおりですが、「未定義の動作」というのは規格が定義していないという意味ですので、処理系によって定義されていれば、予想不能な事態は避けられます。 もちろん、その処理系によって定義された内容が、自分の期待通りであるかどうかはまた別の問題ですが。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.6

「C++ では」というか「C++ でも」間違った (というより未定義動作なので何が起こるかは不明な) 処理です. C でも未定義動作ですので.

  • suseimei
  • ベストアンサー率35% (17/48)
回答No.5

回答を補足いたします。 > でもこのアセンブラを見ると、やってはいけないことをやっているということが > 分かるんですか? 慣れてくるとある程度分かります。ただし、コンパイラー(とバージョン)ごとに異なると思います。この例の場合、アドレス(415700h) に注目してください。これは、一般にコードが置かれる領域ですから、プログラム実行後、この領域に書き込みを行うと、アクセス違反が発生します。 このあたりの背景事情は、Bjarne Stroustrup氏の著作物に詳しく載っています。 説明が足りず、たいへん失礼いたしました。

letrmf
質問者

お礼

>説明が足りず いいえ、そんなことありません。 僕はとても勉強になりました。 慣れれば色々と分かってくることがあるんですね。 僕はまだ 004113C1 C7 45 EC 00 57 41 00 mov dword ptr [str2],offset string "ab" (415700h) の意味が mov dword ptr [str2],0x415700 なのか、それ以外の意味なのかも分かりませんでした。

  • suseimei
  • ベストアンサー率35% (17/48)
回答No.4

> 便利なソフトですね。 無料のVC++ 2005 Express Editionに付属するデバッガーを使っているだけです。 慣れると誰でも使えます。 > でもこのアセンブラを見ると、やってはいけないことをやっているという > ことが分かるんですか? いえ、コンパイラーの特徴が分かるだけです。基本的には、MOV(転送命令)に着目します。メモリへの転送が多い場合、一般的には、それだけ速度が落ちます。 別の方もおっしゃられていますが、コンパイラーはエラーとせずに、救済のために、(自己流で?)解釈している様子がなんとなく分かると思います。 コンパイラーなどに興味があるようでしたら、次のサイト情報を読んでみるとよいと思います。 http://ttoyota.com/freetutorial/cppnovice023.htm

letrmf
質問者

お礼

VC++ 2005 Express Editionのデバッガーを使ってみたくなりました。 ありがとうございました。

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.3

#1 の方も書かれているように、規格上の未定義動作なので、いかなるエラーが起きるかも無保証です。 ありがちなのは、定数が本当にROMに配置される環境で、アクセス違反になることでしょうか。

letrmf
質問者

お礼

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

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

> C++では間違った処理ですか? 間違った処理です。 文字列リテラルは書き換えることができません。本来であれば、ポインタの型がconst char*でなければエラーにすべきところですが、既存コードの救済のために、(意図的に)char*も使える仕様になっています。

letrmf
質問者

お礼

ありがとうございます。 全然エラーが起きないから3バイト以内ならstr2が差すメモリを書き換え可能かと疑っていましたが、間違ったソースだったんですね。