• ベストアンサー

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が差すメモリを書き換え可能かと疑っていましたが、間違ったソースだったんですね。

関連するQ&A

  • [C]char型のダブルポインタ

    粗雑で申し訳ありませんが、 以下のソースをコンパイルできましたが、 うまく実行できません。 自分なりに間違いがないと思うのですが、 間違い等をご指摘頂ければ助かります。 #include <stdio.h> void func(char **ptr) ptr[][10] か (*ptr)[] なら通る *ptr[] は通らない { printf("----- func -----"); printf("%s\n", *ptr); printf("%c\n", **ptr); putchar('\n'); } int main(void) { char str[5][10] = {"AAAAA", *str[] にすると func で **ptr で通る "BBBBB", "CCCCC", "DDDDD", "EEEEE", }; printf("----- main -----"); printf("%s\n", *str); printf("%c\n", **str); putchar('\n'); func(str); return (0); } 実行結果 ----- main ----- AAAAA A ----- func ----- Bus error (core dump) 関数への受け渡しで、型が違うというお叱りを受けますが、 コンパイルはできました。 コンパイラはCCです。 ではよろしくお願いします。

  • int とcharの使い方と違い

    教えてgooに投稿するのは初めてですがよろしくお願いします。 最近C言語を勉強し始めた初心者です。 ネットのサイトを見て独学でしています。 過去スレッドをさらっと見て聞きたいことが書いてなかったので投稿します。(同じスレッドがあったらすみませんOTL) #include<stdio.h> int main(void) { int str='a'; printf("str=%c\n",str); return0; } この上のプログラムでは str=a と出て、エラーが出ずにコンパイル出来ました。でも、 #include<stdio.h> int main(void) { int str[]="abc"; printf("str=%s\n",str); return0; } とするとエラーが出ます。 int とcharの使い方と違いについて詳しく教えてほしいです>< お願いします。

  • atoi

    123と表示されたいんだけど、コンパイルエラーです。 この場合、atoiの引数して、str[3]の文字を入れる変数をもう1つ用意するしかないですか? キャストでうまくできる方法があったら教えてください。 strという変数の"abc123"という文字は変化させたくないんです。 #include <iostream.h> main(){  int i;  char str[] = "abc123";  i = atoi(str[3]);  printf("%d\n", i); }

  • C++のnew演算子で動的確保

    new()を初めて使ってみました。 エラーは無かったけど、間違いがあったら教えてください。 #include <iostream.h> main(){ int n = 0; char* str = "mojisuufumei"; char* myValue; while(!str[n])n++; myValue = new char[n+1]; strcpy(myValue, str); cout << myValue; delete []myValue; } char*型のmyValueを動的確保したつもりです。 myValue[0], myValue[1], … のそれぞれの値を unsignedにして宣言したい場合はどうしたらいいんですか? #include <iostream.h> main(){ int n = 0; char* str = "mojisuufumei"; unsigned char* myValue; while(!str[n])n++; myValue = new unsigned char[n+1]; strcpy(myValue, str); cout << myValue; delete []myValue; } だと、strcpy()のとこでコンパイルエラーでした。

  • 文字列のコピー

    C言語で、文字列をコピーする関数で、処理が止まってしまいます。 以下が実行したプログラムです。 #include<stdio.h> #include<string.h> void copy(char *str2 ,char *str3){ strcpy(str2,str3); } void input(char* str){ scanf("%s",str); } int main (void){ char *a; char *b="TEST"; printf("%s\n",b); input(a); copy(b,a); printf("%s",b); return(0);} copy(b,a);で処理が止まってしまいます。入力した文字列を表示できるように、御指摘お願いします。

  • 配列について

    初歩的な質問ですいませんが、質問よろしくお願いします。 ◎1----------------------------- #include<stdio.h> int main(void) { char ss[10]="AB"; printf("ss=%s\n",ss); return 0; } ------------------------------------ ◎2-------------------------------- #include<stdio.h> int main(void) { char ss[10]; ss[0]='A'; ss[1]='B'; ss[2]=0; printf("ss=%s\n",ss); return 0; } ----------------------------------- ◎3------------------------------- #include<stdio.h> #include<string.h> int main(void) { char ss[10]; strcpy(ss,"AB"); printf("ss=%s\n",ss); return 0; } ----------------------------------- ◎4------------------------------- #include<stdio.h> int main(void) { char ss[10]; ss="AB"; printf("ss=%s\n",ss); return 0; } ---------------------------------- 以上4つのプログラムで、◎2と◎3は正常に動くと理解できたのですが、何故、◎1は正常に動き、◎4は「'const char [3]' から 'char [10]' に変換できません。」といったようなエラーが出てしまうか分かりません。 教えていただければ嬉しいです。

  • abcdとキーボードで打ったらdcbaと表示されるプログラム

    C言語に関しては初心者です。 メイン関数は変更せずに行います。 /* reverse.c: reverse a given string */ #include <stdio.h> #include <string.h> void reverse(char *); /* プロトタイプ宣言 */ int main(void) { char str[100]; scanf("%s", str); reverse(str); printf("%s\n", str); return (0); } void reverse(char *s) { char n; int i; char str; n = strlen(str) - 1; for(i=0,i++,i<=100) { s[i] = *s[n-i]; printf("%s\"s[i]); } }

  • char *(*)[3];について

    #include <iostream.h> main(){ char *(*pp)[3]; printf("%lu", sizeof(char*)); printf(" pp%lu", pp); printf(", ++pp%lu", ++pp); } これを実行して 4 pp6660000, ++pp??????? のように表示されたときに、???????の部分が何になるか考えました。 ppはchar[3]を指すポインタのポインタだから、ppをインクリメントすれば、「char[3]を指すポインタ」の大きさだけ大きくなるはずだから、「char[3]を指すポインタ」の大きさである4バイト大きくなるはずだから 4 pp6660000, ++pp6660004 になると思いました。 どこを勘違いしていてどう考え直せばよいか教えて下さい。

  • C言語 strcmp 半角スペースがあるとだめ?

    C言語 strcmp 半角スペースが文字列に含まれている場合 文字列の比較がうまくいきません。半角スペースがあると比較できないのでしょうか? プログラム //strcmp #include <stdio.h> #include <string.h> int main(void){ char input[256]; char str[] = "HelloWorld!";   //char str[] = "Hello World!";だとうまくいかない。 printf("%s\n>", str); scanf("%s", input); if ( strcmp(input, str) == 0){ printf("同じです。\n"); }else{ printf("違います。\n"); } return 0; }

  • sscanfって・・・。

    #include<stdio.h> main() { char str[] = "aiueokkakikukekossashisusesottstituteto"; char a[20], b[20], c[20], d[20]; sscanf(str,"%[^k],%[^s],%[^t],%[^\0]",a,b,c,d); printf("%s\n",a); printf("%s\n",b); printf("%s\n",c); printf("%s\n",d); } 上のプラグラムでaはaiueoとちゃんと出力されるのですがb,c,dがちゃんと出力されないのはなぜでしょうか?与えられた文字を代える以外によい方法があれば教えてください。

専門家に質問してみよう