• 締切済み

「配列名はポインタ」という表現は間違っているのでは?

よくネット上で、「配列名はポインタ」というような表現が出てきますが、 「配列名はポインタになっているのである。 http://www.wakhok.ac.jp/~kanayama/C/99/node111.html」 このような表現は、間違っているのではないでしょうか? C言語を作った本人によるK&Rのp.121には、次のように書かれています。 配列名とポインタの間には、心に留めておかなければならない違いが一つある。 ポインタは変数であり、したがってpa=aやpa++は意味のある演算である。   ←(1) しかし配列名は変数ではない。したがって、a=paやa++のような構文は正しくない。 ←(2) (1)より、 ポインタは、変数である ので、この対偶をとると、 変数でないものは、ポインタではない  ←(3) となります。 (2)より、 配列名は、変数ではない  ←(4) となるので、 (3)と(4)から、 配列名は、ポインタではない ←(5) となります。 そうすると、「配列名はポインタ」というような表現は間違っていることになり、誤解の元になるので、改めるべきではないかと思うのですが。

みんなの回答

  • ricardo_
  • ベストアンサー率19% (14/72)
回答No.9

stdio.h に次のように書いて有ります。 extern int puts(const char *);  「括弧内には、文字列の先頭アドレスを入れる」と解釈すればいいのですが、「char * は、char データのポインタ。よって括弧内にはポインタが入る」と考え、「文字列の名前はポインタ」と言う図式になるのでは無いでしょうか。

template_i
質問者

補足

K&Rのp.121の5行目に次のように書いて有ります。 ”配列名が関数に渡されるとき、渡されるのは配列の先頭アドレスである。” (もちろん、ここでの「配列の先頭アドレス」とは配列の先頭要素のアドレスです。)

  • ricardo_
  • ベストアンサー率19% (14/72)
回答No.8

 次の表現が正しいのでは無いでしょうか。 ・配列名はアドレスを示す定数 ・ポインタはアドレスが書かれている変数  これを次のように書き直してみましょう。 ・配列名はアドレス ・ポインタはアドレス  すると、配列=アドレス=ポインタ と成り、配列=ポインタ と言う図式が出来てしまうのでしょう。  次のように考えれば、配列名は右辺値にしかならないのが分かります。 char MSG[] = "algo" ; /* 0x1000 から配置されているとする*/ char *p ; p = MSG ; /* 文字aのアドレス */ p = MSG + 1 ; /* 文字bのアドレス */ MSG = p ; /* 0x1000 = p のようなもので、出来ない */ MSG++ ; /* 0x1000++ のようなもので、出来ない */

template_i
質問者

補足

個人的には、配列名は、 2)先頭要素のアドレスを値に持ち、 3)アドレス演算の対象になり得る、 4)右辺値である という表現が良いと思っています。 「配列名はポインタ」という表現は、この板でもときどき質問される、 「配列とポインタの違いが良く分かりません」 という無用な混乱を招く諸悪の根源以外の何物でもないと思います。 そうすれば、 printf("%c\n", *("ABCDE" + 3) ); char* p = "ABCDE"; printf("%c\n", *(p+3) ); printf("%s\n", p+3); といった記述を見ても、混乱しないですむように思います。

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

「配列名はポインタになっている」と「配列名はポインタである」とは違うことを言ってます. だから, 後者が誤っていたとしても前者が誤っているとは必ずしも言えません. 「ポインタは変数であり」も, 冷静に考えると意味がよくわからん. まあ K&R は「原著はいい」って言われるくらいだからこんなもんかもしれんけど.

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.6

>>mに100が入っていると仮定すると100番地にはm[0]が104番地にはm[1]が、108番地にはm[2]の領域があることになります(勿論厳密にはこの領域はなくてもよい)。 この話と >>"abc"の先頭番地が200であると仮定するとm[0]には200が、m[1]には204が、m[2]には208が入っています。 この話とは、別々の話です。したがって、 >char m[3][4]={"abc","def","ghi"}; > >と配列mを定義するとき、100番地にm[0]の領域があるならば、 >"abc"の先頭番地は200ではなくて100なのではないでしょうか? 誰も"abc"の先頭が200とは言っていません。

template_i
質問者

お礼

>この話と…この話とは、別々の話です。 そう言えばそうですね。解説ありがとうございます。 #4さん、勘違いしてすみません。

回答No.5

よくネットでというのはどの程度の頻度でそういう記載があるのか知りませんが、どういう表現を使うのかどうかはその人の自由です。 たとえば、嘘が書いてあったとしてもそれを他の人がどうこう言うべきではありません。 表現の自由です。 それをふまえた上で、 C言語の入門者が一番最初につまずくのは配列やポインタ、構造体等で、教える方は、いかにそれらを理解させるのかに苦心します。それらの過程で、あまり正確ではない説明や、本来は正しくない説明が入ることがありますが、それらは仕方がないことです。 極端な話、たとえ、教科書に間違った記述を書いていても、もしそれに生徒が興味を持ち、最終的に正しく理解できるのであれば、教科書としては十分目的を満たしていると思われます。

回答No.4

おっしゃるとおり 「配列名はポインタである」 という表現は厳密に言えば間違っています。 しかし、配列名をポインタと同じように考えることで、ポインタと配列の解説がしやすくなることもあります。 例えば、1次元配列名はポインタ(厳密には違う) 2次元配列名はポインタのポインタ(厳密には違う) 3次元配列名はポインタのポインタのポインタ(厳密には違う) と考えると、 例えば、char型は1バイト、ポインタは4バイトとして、以下のプログラムを解説すると char m[3][4]={"abc","def","ghi"}; printf("%c",*(*(m+1)+2)); では mはcharへのポインタのポインタと考えます。 そして、m[0],m[1],m[2]はcharへのポインタと考えます。 メモリイメージを説明すると(厳密にはここの説明は間違っています) mに100が入っていると仮定すると100番地にはm[0]が104番地にはm[1]が、108番地にはm[2]の領域があることになります(勿論厳密にはこの領域はなくてもよい)。"abc"の先頭番地が200であると仮定するとm[0]には200が、m[1]には204が、m[2]には208が入っています。 m+1ではmはポインタのポインタなので、実際には4が加えられ、104となります。 *(m+1)は104番地の内容なので、204となります。この204はcharへのポインタなので、+2すると、2が足され、206になります。つまり*(*(m+1)+2)は206番地の内容となり、fが出力されます。 xxのポインタに1を足すとxxのサイズ分足されることを利用して、上記のような説明をすることがあります。

template_i
質問者

補足

>mに100が入っていると仮定すると100番地にはm[0]が104番地にはm[1]が、108番地にはm[2]の領域があることになります(勿論厳密にはこの領域はなくてもよい)。 >"abc"の先頭番地が200であると仮定するとm[0]には200が、m[1]には204が、m[2]には208が入っています。 K&Rのp.139に、次のような配列anameの占めるメモリ領域が図解されています。 char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" }; このとき確保されるメモリ領域は、15バイト×4=60バイトであり、先頭から数えて 0バイト目にchar型配列"Illegal month"の先頭要素の文字定数'I'が、 15バイト目にchar型配列"Jan"の先頭要素の文字定数'J'が、 30バイト目にchar型配列"Feb"の先頭要素の文字定数'F'が、 45バイト目にchar型配列"Mar"の先頭要素の文字定数'M'が 格納された図が載っています。 char m[3][4]={"abc","def","ghi"}; と配列mを定義するとき、100番地にm[0]の領域があるならば、"abc"の先頭番地は200ではなくて100なのではないでしょうか?

回答No.3

以下,関数の引数の場合を除きます。 ・配列型は (派生) 型である →ISO/IEC 9899:1999 (以下IS) 6.2.5 Types / Paragraph 20, Enumeration 1 ・ポインタ型は (派生) 型である →IS 6.2.5 Types / Paragraph 20, Enumeration 5 ・型Tの配列型として宣言した変数xは型Tの配列型のオブジェクトにつけられた識別子となる →IS 6.7.5.2 Array declarators ・配列型のオブジェクトがsizeof, 単項&演算子を除く演算子の引数になる場合,「先頭要素へのポインタ」という値に変換される。 →IS 6.3 Conversions - 6.3.2.1 Lvalues, arrays, and function designators / Paragraph 3 配列型の変数xがあった場合,それはあくまで配列型を持っています。 なので,xは「容易にポインタ値になる配列型の変数」ではあるものの, 「ポインタ型を持った変数」ではありません。 # 仕様書上「変数」という言葉は出てこないし,「識別子」と「オブジェクト」は別物ですが,そのあたりは無視します。 また,代入ができないのは, ・配列を代入できないのは,変更可能な左辺値でないオブジェクトに代入しようとしているから →IS 6.3.2.1 Lvalues, arrays, and function designators / Paragraph 1 →IS 6.5.16 Assignment operators / Paragraph 2 ・++演算子等が使えないのは,左辺値でないオブジェクト (ポインタ値) に代入しようとしているから →IS 6.3.2.1 Lvalues, arrays, and function designators →IS 6.5.2.4 Postfix increment and decrement operators / Paragraph 1 →IS 6.5.3.1 Prefix increment and decrement operators / Paragraph 1 なお,関数の引数に出てきた場合は話が異なります。 関数の引数が配列型として宣言されていた場合, その要素型へのポインタ型として宣言されているとみなされます。 →IS 6.7.5.3 Function declarators (including prototypes) / Paragraph. 7

回答No.2

なるほど、どちらもアドレスを指しているので私もすっかり配列名はポインタと思い込んで私もそのように表現したりしていましたが、そもそもの定義はアドレスを指す変数がポインタだったのですね(K&R本は読んだつもりだったのですが...)。配列名の指すアドレスは変更できないから別物と書いてあったとは...なるほど。 改めます。

回答No.1

配列は「ポインタ定数」とでも言えばいいでしょうかね。 ポインタとして機能しますが値を変更できません。

関連するQ&A

  • 配列表現とポインタ表現

    配列とポインタの2通りの表現で表せる場面によく遭遇します。例えば、pというdouble型の配列に乱数を10個発生させて格納したい時など、 for(i=0;i<10;i++) *(p+i) = (double) rand(); for(i=0; i<10; i++) p[i] = (double) rand(); のように、配列とポインタの2通りの表現が考えられると思いますが、複雑な場合などは特に、見た感じは配列のほうが分かりやすいと思います。 まだ、C言語の初級から中級向けの本しか読んでいないのですが、標準関数の多くがポインタを引数や返り値としていることを知りました。わざわざポインタ表現にすることの意義は、実行速度が上がることと、標準関数の多くがポインタを引数や返り値としているからと理解して良いのでしょうか。

  • ポインター変数名と配列名は同じでよいのでしょうか?

    C言語で int *data; data[0]=0x15; という記述を見ました。 私にはdataはポインター変数名 であり配列名に思えます。 (ポインター変数へポインター定数を代入するには data = data; ???) しかし入門書にはこんな例はありませんでした。 私の解釈とはちがった意味があるのでしょうか? 教えてください。

  • 配列を使わずに、変数名を動的にループで回したい

    配列を使わずに、変数名を動的にループで回したい 大学の課題をやっている途中に思ったことなのですが……。 プログラム中に同じ型の変数p1,p2,p3があり、それぞれの変数にループで同じ処理を施したいと思っています。 しかし、「p(i)」や「("p" + i)」などと、変数どうしを別のものとして認識させようとしても、コンパイラから警告が出て上手くいきません。 配列を使えば、このような処理が出来ることはわかるのですが、使う変数名を指定されてしまっているために、今回は配列を使えません。 出来るか出来ないか、出来る場合は方法を知りたいです。 後、今回はC言語のプログラムで利用出来るかが自分にとっての問題なのですが、C言語以外のこのプログラミング言語なら出来る、とかの情報もこの機会に知りたいです。 お答え出来る範囲でいいので、もしよければ回答お願いします。

  • 二次元配列とポインタについて

    関数内の変数宣言にて (1) int *( p[ 5 ] ); (2) int ( *p )[ 5 ]; の違いを教えて下さい。 (1)は *p[ 5 ] と同義のようで int実体のポインタとなるp[ 0 ]からp[ 4 ]の 配列が作られるようです。 つまり領域に作られるのは 5つの連続したint型へのシングルポインタであり その他のint実体やダブルポインタは 領域に作られないと認識しております。 (2)との違いが分かりません。 領域では実際に何が作られるのかという点と このように演算の優先順位がある場合に どのような順番で解釈すればよいのかという点について ご説明頂けると助かります。 ではよろしくお願いします。

  • char型のポインタ配列に変数の値の代入できる?

    c言語でchar型のポインタ配列に変数の値を代入できるのでしょうか? 例えば int A[10]={1,2,3,4,5,6,7,8,9,10}; char *C[10]; のCに配列Aの中のデータを文字列として入れたいのです。 C[0]="A[0]"としてもA[0]という文字列が代入されてしまうだけなので… よろしくお願いします。

  • 配列とポインタの違いについて

    書籍などで配列とポインタの違いについて勉強していますが、未だによくわかりません。 C言語における配列とポインタの違いについて教えて頂けますでしょうか?

  • 多次元配列のポインタ渡し

    C++を使用しています。 多次元配列を関数の引数として渡したいとき、関数側では void A::Func(int a[10][20][30])~ 呼びだし側では Finc(a); とやればいいのはわかります。 お聞きしたいのは、仮引数として呼び出された配列(上でいうa)をクラスのメンバ変数として保持したい場合の方法です。 aは先頭アドレスなのでそこを差すポインタを受ければいい、っていうことはわかりますが、 この方法ですと、受けたメンバ変数が配列みたいに[]を使ってアクセスできません。 (メンバ変数のポインタは配列じゃないから当然ですよね) これを通常の配列みたいに扱えるようにするにはどうしたらいいでしょうか。

  • ポインタ変数とポインタのポインタ

    ポインタ変数の宣言 char *a[]; をしたとき僕の中では a[0],a[1]...という、ある文字列A,B,C...の最初のアドレスを指すポインタが、配列になっているものを宣言していると理解していました。 しかしこの次に、ポインタのポインタが出てきました。僕はこれを、 ある変数を指し示すアドレスのアドレスである、と理解しました。 この2つは1つめはいくつかのアドレスを指し示すもの、2つ目は1つのアドレスを指し示すものであるとして、僕の中で異なったものであると理解していましたが、参考書「C標準コースウェア」によると プログラムにおいて、関数でポインタ配列を受け取るときchar *p[]はchar **pとしてもよい と書かれており、またその実例として、 (9-5) #include <stdio.h> void disp (char *p[],int n){ int i; for (i= 1;i<n;i++){ printf("%s\n",p[i]); } } int main(void){ char *girl[] = {"Arica","Candy","Lisa"}; disp (girl,sizeof(girl)/sizeof(girl[0])); return 0; } というプログラムが書かれていました。 ここで一気に訳が分からなくなりました。 char *girl[] = {"Arica","Candy","Lisa"}; と宣言されているため、 girl[0]はAricaという文字列の最初のアドレスを指すポインタ、 *girl[0]はAricaという文字列を直接指し示していると解釈しています。 girlは{"Arica","Candy","Lisa"}という文字列の配列の最初のアドレスを指し示していると考えました。 sizeof(girl)を使った時に不思議なのですが、 girlはどのように配列の終わりを理解しているのでしょうか? (配列の要素数を渡していない点が不思議です。) また、 disp側が受け取ったのは*girl[]であり、いくつかのポインタの配列ですが、渡したものはgirlという要素数がないポインタ1つだけです。 そして最初の疑問が出てくるわけですが、*p[]を**pと書きかえてみると、 文字列のアドレスを示すgirlという名の1つのポインタを渡すと、pという名のポインタのポインタで受け取るというのも、よくわからなくなっています。 おそらくポインタ配列に対する理解がどこかでずれているようですが、自分でどこがわからないのかわからなくなっています。 どうかご教授ください。

  • 二次元配列のポインタについて教えて下さい

    今日はCの配列のポインタについて質問いたします、宜しくお願いします。 1次元の配列からは、 =================================== int *p , a={1,2,3}; p = &a; printf("%d\n" , p[1] ) ; printf("%d\n" , *p[1] ) ; ===================================== でaの値がとれますが、 二次元の配列では下のような書き方ではエラーになります。 何故でしょうか、どう書いてやればいいのでしょう。 ===============================================  printf("%d\n" , pbb[1][1] ) ;  printf("%d\n" , *pbb[1][1] ) ; =============================================== 宜しくお願いします。

  • ポインタを用いた配列

    コマンドラインで入力した入力した3つの数字に配列ポインタを付けて 線形リストにし、それらを表示したかったのですが、エラーが出て分かりません。 3、4年前に一度やったことはありましたが、いざやってみると全然できませんでした。 どこをどう直したら良いのか、ご教授よろしくお願いします。 class Node1 { public static void main(String arg[]) { String num = br.readLine(); a1.new Node(num); a2.new Node(num); a3.new Node(num); a1.next = a2; a2.next = a3; System.out.println("a1 = " + a1.new); System.out.println("a2 = " + a2.new); System.out.println("a3 = " + a3.new); } }

専門家に質問してみよう