char* 宣言での配列要素アクセスについて

このQ&Aのポイント
  • char*型で宣言したポインタ変数に対して配列の要素でアクセスすると落ちてしまいます。
  • char*型とchar配列の違いによる問題であり、ポインタ変数には文字列の先頭アドレスが格納されるため、要素への変更は不可能。
  • pA[2]のアドレスを確認すると、pAのアドレス[+2]を指しているが、文字列リテラルは定数領域に保存されるため書き換えができない。
回答を見る
  • ベストアンサー

char* 宣言での配列要素アクセスについて

char* 宣言での配列要素アクセスについて char*型で宣言したポインタ変数に対して 配列の要素でアクセスすると落ちてしまいます。 理由がよくわかりません。 以下コードになります。 (コードA) char* pA = "123456789"; pA[2] = 'A'; 以下のコードの場合は問題ありません。 (コードB) char pB[] = "123456789"; pB[2] = 'B'; 両者の違いがいまいちよく分かりません。 コードAでpA[2]のアドレスを確認すると、 pAのアドレス[+2]を指しているので問題ないともうのですが、 間違っているのでしょうか? pAに対して特別な操作はしていません。 ご教授お願いいたします。

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

  • ベストアンサー
  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.1

>char* pA = "123456789"; >pA[2] = 'A'; メモリ上のどこかに"123456789"が格納され、その先頭アドレスがpAに格納されます。 メモリ上のどこかに格納されている"123456789"は「文字列定数」となります。 「定数」なので、通常は書き換えは許可されません。 >char pB[] = "123456789"; >pB[2] = 'B'; メモリ上(ローカル変数ならたいていはスタック領域上)に10バイトの領域を確保し、 その領域の初期値として"123456789"を格納しています。 こちらは通常内容が変化するものとして使用されるので、書き換えは可能です。 http://www9.plala.or.jp/sgwr-t/c/sec10-3.html の【文字列リテラル】を参照されるとよいでしょう。 char* pA = "123456789"; char* pB = "123456789"; と記述した場合、指している文字列は同じですからコンパイラの最適化によってはpAとpBが同じアドレスを指す場合があります。 さて、文字列リテラルの書き換えを許可していたとて、*(pA+1) = 'B'が可能だった場合にpBが指す文字列はどうなるでしょう?

kakedasic
質問者

お礼

Wr5様 回答ありがとうございます。 よく分かりました。 参考URLもわかりやすかったです。

その他の回答 (3)

回答No.4

コードAの場合、文字列を格納するだけの領域を確保したうえで文字列をその領域にコピーするようにしなくてはなりません。 char* pA = (char*) malloc(10); /* 文字列のサイズ+1 のバッファを確保 */ strcpy(pA, "123456789"); pA[2] = 'A'; コードAのように char* pA = "123456789"; としてしまうと、読み取り専用メモリに文字列"123456789"が格納されてしまい、その領域を書き換えようとすると落ちてしまいます。

kakedasic
質問者

お礼

ryoyama様 回答ありがとうございます。 参考になりました。

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

コードA のように書いた場合, pA がさす文字列リテラルを変更してはいけないことになっています. イメージとしては, コードA では「文字列リテラル "123456789" はプログラムが直接管理しないどこかにあって pA はその領域を指す」, コードB は「プログラムで必要な領域を pB として確保しそこに文字列リテラル "123456789" と同じものを入れておく」という感じでしょうか. 本来文字列リテラルは「定数」であり, その型は const char [] であるべきものです. C では const のつかない char [] ですが, これは「そもそも const というものが存在しなかったはるか遠い過去」との互換性によるものであり, 「変更できないものである」と考えるべきです. ちなみに C++ では const char [] だけど特例で char * な変数にも代入できる (がそのような操作はすべきではない).

kakedasic
質問者

お礼

Tacosan様 回答ありがとうございます。 参考になりました。

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.2

コードAは、(一般的に)書き換え不能領域に配置される文字列"123456789"の先頭アドレスをpAに与えます。 で、書き換え不能領域を書き変えようとするのでメモリ保護例外が出ます。 #環境によってたまーに例外があるので注意 コードBは、配列pBを"123456789"の値で初期化します。 こちらは書き換え可能領域内にあるので、自由に書き換えられます。 従って、配列のようにアクセスしたのが原因ではなくコードAでは*(pA+2)='A';でも同様にコケます。

kakedasic
質問者

お礼

D-Matsu様 回答ありがとうございます。 参考になりました。

関連するQ&A

  • C言語の型と配列

    char* str[10]={"a","b"}; char* str2="c"; としたときにstr=str2とすると 型が合わないといったエラーが出ます。 でもstrって結局はポインタの配列の先頭要素のアドレスですよね。 ポインタにポインタを入れているので通るのかなと思ったんですけど、 配列で宣言するとポインタにも型がつくのでしょうか? この例だと strは char * を10個持つ配列をさすポインタ  で、 str2はchar *をさすポインタ みたいなかんじです。 質問の意味がわかりにくいですが、ご指摘をいただければ補足しますので よろしくお願いします。

  • 配列の要素追加について

    配列Aがあり、その配列に別の要素を加えた配列Bを作成するためにはどのようにすればよいのでしょうか? Aをコピーしたものに要素を一つ加えてと考えたのですが。。。 つまり char[] a = {'a', 'b', 'c'}; char ch = 'd'; とあったときに、aにchを加えて char[] b = {'a', 'b', 'c', 'd'}; を作成したいです。 よろしくお願い致します。

    • ベストアンサー
    • Java
  • 配列の要素を取得する場合?

    char a[] = "aaaaa"; char *b = "aaaaa"; という宣言があるとき、配列aなら、 sizeof(a)/sizeof(a[0]); で、全体の要素数を求められますが、bの方法で初期化をして、 sizeof(b) / sizeof(b[0]) をやっても、変数bのサイズしか求められません。 bの方法でも、最大要素数を求めることはできますか?

  • char *abc[20]

    基本的なことかもしれませんが、ポインタのことを理解していたとおもいきや、また混乱しはじめたので質問させていただきます。 char *abc[20]; この意味は、 char型のポインタを要素とする配列。 char型の要素数20個の配列へのポインタ。 という2つの解釈ができそうなのですが、どちらでしょうか??この2つは違ういみですよね?

  • 配列で混乱しています

    c言語を勉強中のものです。文字列を配列で扱う場合とポインタで扱う場合の違いにこんらんしています。 たとえば char str[] = "hello"; str = "goodby"; のように文字列の再格納はダメですが、 char *str = "helllo"; str = "goodby" は大丈夫です。 また char[100]; scanf("%s",str) は大丈夫ですが char *str; scanf("%s",str) はいけません。この2つの違いは何によるのでしょうか? またポインタはそもそも int a; int *pa; pa = &a; のように、宣言したあとはアドレスを入れる専用のハコのように考えていたのですが、 char *str = "helllo"; str = "goodby" のようにいきなりアドレスでない文字列を代入するのも納得がいきません。誰か解説をお願いします。

  • 配列とポインタと同時に使用する宣言について

    配列とポインタを同時に使用した場合、どのように確保されるのかイマイチ理解しきれていません。 例えば、 char *pointer[10]; と宣言した場合、 pointer[0]="ABC" pointer[1]="DEF" ・・・・ のようにポインタ型を配列で管理する、という風に認識しています。 一方で char (*pointer)[10] とした場合、どのように確保されるのでしょうか?

  • 文字列の宣言(ポインタと配列)

    Cでソケット通信などはできるくらい(もちろんできると言っても私のレベルで) になりましたが、文字列を宣言する際に char *str = "ahaha'; char str[] = "ahaha"; の2種類の違いが今イチしっくりきません。 いろんな参考書でこれの説明はありましたが、でも結局なんなんだ、という感じです。 ポインタで宣言するべき時、配列で宣言するべきときが判断できません。 またポインタで宣言するとエラーが出るけど試し配列で宣言してみたら なんか知らんが動いた、ということも多々ありましたが理由がよくわからなかったです。 よろしくお願いいたします。

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • 配列の要素数を調べる方法

    配列の中にいくつ要素があるか後から調べる方法はあるでしょうか? int test[4][3] = {   {0,1,2},   {0,1,2},   {0,1,2},   {0,1,2}, } ; 上のケースだと、X方向に3、Y方向に4つの 要素があります。しかし、これだとあらかじめいくつ要素があるかを決めて 置かないといけません。 例えば、 int test[] = {0,1,2,3....} みたいに宣言された場合や、 char *test[] = {   "ああああああ",   "いいいいいい",   "うううううう",      ・      ・ //(これは文字列の数を要素数とします) } ; のように宣言された二次元配列などで、後から配列内にいくつ要素があるかを 調べることなどは可能でしょうか?

  • char配列のポインタ消去

    char配列のポインタ消去 こんにちは。C++初心者で、現在ポインタと配列の関係を勉強しています。 以下のコードついて質問があります。 #include <stdio.h> extern void main () { char* str = "hello world."; printf(str); delete str; } コンパイルはうまくのですが、 いつもプログラムが正常に終了できません。 deleteをコメントアウトすると正常に終了します。 理由がわかる方いらっしゃるでしょうか。

専門家に質問してみよう