• ベストアンサー

先頭アドレスとは何ですか?

Cの勉強をするため、Cの勉強用のホームページを読んでいたのですが その中に 「“int a[10];”というふうに配列を宣言した場合、配列名“a”はその配列の先頭アドレスになります」 という一文がありました。これが理解できません。 「アドレス」はメモリ内の、変数が記憶されている場所のことですよね。 先頭アドレスの「先頭」とは一体何に対して「先頭」なのでしょうか。 「一番前」という意味でしょうか。なら何の中で一番前なのでしょう? さらにそのホームページでは 「配列名“a”を式の中に書くと、普通は配列の先頭要素を指すポインタになります」 と言いなおしていましたが、これもよくわかりません。 「先頭要素」とは? 一体何に対する先頭なのでしょう?要素の先頭とは何を指すのかがいまいちピンときません。 初心者にありがちなお恥ずかしい質問ではありますが、ご教授いただければ幸いです。

  • nekoc
  • お礼率94% (128/135)

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

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

例えば、int型が4バイトの場合... int a[10]; のように配列を宣言すると、    +0+1+2+3 a[0]: □□□□ a[1]: □□□□ a[2]: □□□□ a[3]: □□□□ a[4]: □□□□ a[5]: □□□□ a[6]: □□□□ a[7]: □□□□ a[8]: □□□□ a[9]: □□□□ (□1つが1バイトを表しています) のようにメモリ上には配置されます。 ここで、「先頭要素」というのは、配列aの最初の要素のことですからa[0]のことです。先頭アドレスというのは、先頭要素a[0]の最初のアドレス(上の表では+0のところのアドレス)になります。 各要素(a[0]とかa[1]とか)は、それぞれint型ですから4バイトで構成されます。この4つのバイトには、それぞれアドレスがふられているわけですが、具体的に整数値のどの部分がどのアドレスに相当するかは処理系(多くの場合はプロセッサ)に依存します。 例えば、馴染みの深いIntelのプロセッサの場合、通常、次のようになります。 a[0]が0x12345678という値だった場合、 +0: 0x78 +1: 0x56 +2: 0x34 +3: 0x12 のように格納されます。 なお、 > 「配列名“a”を式の中に書くと、普通は配列の先頭要素を指すポインタになります」 の意味するところは、配列型は多くの場合、暗黙的にポインタ型に型変換されます。ポインタ型に型変換された場合、元の配列の先頭要素を指すポインタの値になります。「普通は」とあるのは、そうならない場合もあるということです。具体的には、sizeof演算子や&演算子のオペランド(演算対象)になった場合です。

nekoc
質問者

お礼

わかりやすい図での解説、ありがとうございます。 今までメモリ領域のことを棒グラフのように直線状だと思い込んでいましたが、 バイトの存在をすっかり忘れておりました。 ここで気づくことができてよかったです。 >a[0]が0x12345678という値だった場合 お礼欄に質問を書くのもどうかと思うのですが、 0xと12345678の関係がよくわかりません。(無知ですみません・・・) 今までは1001とか、1002などの単純な数字のアドレスしか見たことがなかったもので・・・ 簡単でいいので、ご説明願えませんでしょうか。 >「普通は」とあるのは、そうならない場合もあるということです。 >具体的には、sizeof演算子や&演算子のオペランド(演算対象)になった場合です。 sizeof演算子や&演算子のオペランド(演算対象)はまだ習っていないのでよくわかっていないのですが、 必ずしも先頭要素=先頭アドレスではないということはわかりました。 今後sizeof演算子等を勉強する際の参考にさせていただきます。 このたびはどうもありがとうございました。

その他の回答 (5)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.6

★0x と 12345678 の関係 ・int 型が 4 バイトのとき、a という配列1つに 0x12345678 という値(データ)を代入したとします。  するとメモリ上では下のようにセットされます。  a[ 0 ] = 0x12345678;    1バイト目が 0x78 の1バイトデータ  2バイト目が 0x56 の1バイトデータ  3バイト目が 0x34 の1バイトデータ  4バイト目が 0x12 の1バイトデータ  となります。 ・なお、上記のメモリ・イメージは Intel 系のプロセッサ(CPU)の場合です。  もしも、Motorola 系のプロセッサ(CPU)の場合は    1バイト目が 0x12 の1バイトデータ  2バイト目が 0x34 の1バイトデータ  3バイト目が 0x56 の1バイトデータ  4バイト目が 0x78 の1バイトデータ  となります。 ・通常のプログラミングでは、あまり意識しなくても良いですが、メモリ・イメージをファイルに  保存したり読み出したりするときは、プロセッサ(CPU)の違いにより上記のような2タイプがあるのです。  Intel 系のプロセッサ(CPU)のような格納方法を『リトルエンディアン』と呼び、  Motorola 系のプロセッサ(CPU)のような格納方法を『ビッグエンディアン』と呼びます。  また、格納方法のことを『バイトオーダー』とか、単に『エンディアン』などと呼びます。 ・『今までは 1001 とか、1002 などの単純な数字のアドレス…。』となっていますが、0x12345678 は  アドレスの数値ではありません。データの内容(値)のことです。  jacta さんの回答にあった int 型(4バイト)に 0x12345678 の値を代入したときのメモリ・イメージが  +0: 0x78  +1: 0x56  +2: 0x34  +3: 0x12  と格納されますよ。って事です。  1バイトずつ 0x78、0x56、0x34、0x12 と順番に格納されるのが『リトルエンディアン』方式で CPU は  Intel のプロセッサとなります。→私の使っているパソコンは Intel 系ですので『リトルエンディアン』  方式でメモリにはデータが格納されているようです。68000系(Motorola) のパソコンならばバイト列が  逆転して格納され、この方法が『ビッグエンディアン』なのです。 ・以上。分かりましたか。 参考文献: ・http://www.atmarkit.co.jp/icd/root/70/5784470.html→『バイトオーダー』 ・http://cai.int-univ.com/sugsi/Lecture/NetProg/chapter3/byte_order.html→『バイトオーダについて』

参考URL:
http://e-words.jp/w/E38390E382A4E38388E382AAE383BCE38380E383BC.html
nekoc
質問者

お礼

穴だらけの理解で申し訳ありません。 しかし、こうしてご指導いただき、とても勉強になります。 ありがとうございます。 0x と 12345678 の関係につきましては、最初Motorola 系のプロセッサの考え方が先にきてしまい、 なぜ一バイト目に大きい方から埋めていくのかなあ・・・と考えておりました。 PCの環境もプログラミングに大きく影響するのですね・・・ 一応知識としては知っていたつもりでしたが、ここでもこんな違いが生まれるとは知りませんでした。 このたびはどうもありがとうございました。

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

> 必ずしも先頭要素=先頭アドレスではないということはわかりました。 そうではなく、必ずしも配列名=先頭アドレスではないということです。 確かに、必ずしも先頭要素=先頭アドレスではないのですが、それはまた別の話です。

nekoc
質問者

補足

基本的なところを間違えていましたね…お恥ずかしいです。 間違えたまま覚えるところでした。 ご指摘、どうもありがとうございました。

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

> 0xと12345678の関係 0xは、16進数のことです。0~9, a~f(大文字も可)の16種類の文字を 使って数値を表現します。 0x12345678と書くと、「千二百三十四万五千六百七十八」ではなく、 1*(16の7乗)+2*(16の6乗)+3*(16の5乗)+...+7*(16の1乗)+8 を表わします。

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

int a[10]; と配列を定義すると、メモリ中のどこかのアドレスに a[0]~a[9]の10個の要素を連続して配置します。 このとき、配列名aは、配列の先頭要素であるa[0]を 格納しているアドレスと同義です。

nekoc
質問者

お礼

回答ありがとうございます。 物に例えるなら、 部屋の中に番号の書いたダンボール箱を0~9番の順に置いて、 番号の若い順から前→後ろと定義していく感じでしょうか。 0番の置いてある場所が畳の一枚目であれば、 一番目の畳が先頭アドレス(0~9番のダンボールの代表住所) になる・・・みたいな感じで頭に置き換えて考えています。 一応自分でも調べてみたのですが、「先頭アドレス」ではIT単語集にひっかからず困っておりました。 解説していただけてとても助かっています。

  • suzukikun
  • ベストアンサー率28% (372/1325)
回答No.1

>先頭アドレスの「先頭」とは一体何に対して「先頭」なのでしょうか。 a[10]としてメモリ上に取られる領域の「先頭」になります。 >「先頭要素」とは? この場合だとa[0]になります。配列の「先頭」です。構造体なども同じように表せるので「先頭要素」という言い方になります。

nekoc
質問者

お礼

回答ありがとうございます。    ←使用 メモリ領域→     0 1 2 3 4 5 6 7 8 9 前 ■■■■■■■■■■ 後ろ   ↑    ↑  ここ    a=10 こういう感じでよろしいのでしょうか? 何をもってして前とするのか後ろとするのかはよくわかっていないのですが・・・ 添え字の順番で一番小さいのが先頭・・・だと思います。 >構造体なども同じように表せるので「先頭要素」という言い方になります。 なるほど! 先頭アドレスと先頭要素の二種類の呼び方があるのはそういうことだったのですね。 このたびはどうもありがとうございました。

関連するQ&A

  • 教えてください3

    int型配列a[5]={111,222,333,444,555}を宣言し、この配列の各要素をポインタptを用いて表示せよ。 ただし、プログラム中でポインタptは常に配列の先頭の要素のアドレスを保持しているものとする。 <実行結果> a[0]=111 a[1]=222 a[2]=333 a[3]=444 a[4]=555

  • 関数の仮引数は宣言か式か

    ”関数の仮引数の宣言”は”変数の宣言・定義”と同じように”宣言”と明確に考えるべきなのか”関数の仮引数”を”式”と考えて良いのかという問題です。 私が迷ってしまったのは"配列を仮引数"にとった例です。 規則として ”int a[ ] が int *a と同じ意味になるのは、唯一、関数の仮引数の宣言のばあいだけである”という規則がありますが、これは”関数の仮引数の宣言”は変数の宣言・定義”と同じように”宣言”と明確に考えている例だとおもいます。 int a[ ] が int *a と型名 変数名(引数名)と宣言の形をとっているので当然だと思いますが、一方 配列は、式の中で「先頭へのポインタ」によみかえられる。               ↓ 関数の引数は式なので、配列は「先頭へのポインタ」に読み変えられる←引数部分を”宣言”ではなく”式”と捉えてる←ここが私の迷っているところ               ↓ よって、関数に渡ってくるのは、結局はつねにポインタだ。 という説明もあります。 私の今までの理解ではc言語では”宣言の部分”と”式”の部分は明確に区別されるものと考えていました。”宣言部分の初期化の="と”代入の=”とは明確に区別されていました。 それと同じように”関数の仮引数の宣言”は”宣言部分”と捕らえるのか”式”ととらえるのか ”宣言”と”式”が私の頭の中混乱しています。 宜しくお願いします。

  • アドレスの計算が合わない

    C++でポインタの勉強をしています。 その中でアドレスについての以下のような記述がありました。 <例1> struct { int a; int b; int c; } oshiete; cout << &oshiete.a << endl; cout << &oshiete.b << endl; cout << &oshiete.c << endl; このように、構造体の中で宣言された変数の領域は連続した場所を確保するというものでした。これの実行結果は以下の様になりました。 0013FF5C 0013FF60 0013FF64 int型のバイト数は4でしたので、それぞれの変数の先頭アドレスは4つ間隔になっています。しかし、これの2つ目の変数bをshort型に変えても同じ結果が返ってくるのです。short型のバイト数は2です。 <例2> struct { int a; short b; int c; } oshiete; cout << &oshiete.a << endl; cout << &oshiete.b << endl; cout << &oshiete.c << endl; 結果: 0013FF5C 0013FF60 0013FF64 そして変数aもshort型にすると、やっと納得のいく結果になりました。 <例3> struct { short a; short b; int c; } oshiete; cout << &oshiete.a << endl; cout << &oshiete.b << endl; cout << &oshiete.c << endl; 結果: 0013FF60 0013FF62 0013FF64 なぜ<例2>ではint, short, intの順で宣言したのにアドレスが全て4つ間隔なのでしょうか?例えば先頭アドレスが0013FF5Cであるなら、 0013FF5C 0013FF60 0013FF62 のように1つ目と2つ目のアドレス差は4、2つ目と3つ目のアドレス差は2になるはずだと思うのですが。

  • 配列変数のアドレス

    C言語で、配列を宣言した場合、 char a[] = "hoge"; 変数aは初期化された配列の先頭番地を指しているのですが、この時、 変数aの中身も変数aのアドレス(&a)ともに上記の配列の先頭番地になっているようなのですが、 これによって、配列の変数aは読み取り専用で、書き換えられないということにしていると思っていいのでしょうか? ちなみに、 char *p = "fuga"; とした場合は、pと&pのアドレスは異なって、pの中身は書き換えられます。

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

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

  • 複雑なポインタの定義

    #include <stdio.h> typedef int *(*i[3])[2]; int main() { int a = 0; int b = 1; int c = 2; int d = 3; int e = 4; int f = 5; int *u[2] = {&a, &b}; int *v[2] = {&c, &d]; int *w[2] = {&e, &f}; int **pp[3] = {u, v, w}; /* arrayをi型(intへのポインタを要素とする2個の配列へのポインタを要素とする3個の配列)として宣言 */ i array; /* この部分でarrayを使って変数a~fの内容を表示したい */ } 上記の最後の部分で、typedefで定義したi型の変数arrayを使って、変数a~fの内容を表示させたいのですが、その上で定義している諸々の変数との型の不整合が起こって、どういう記述をしたらよいか分かりかねています。要は、複雑なポインタの型定義をした場合の使い方がよく分からないのです。 御経験のある方は、御教示願えれば助かります。

  • C言語の型と配列

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

  • 複雑なキャスト

    "(int の要素数3の配列) へのポインタ" を意味するキャストはどのような書き方になりますか? (int*[3]) かと思ったのですが、コンパイルエラーになります。 参考までに、ソースをのせておきます。 pai や ai のようなよくわからない変数名は、 型が複雑なためそこだけハンガリアン記法っぽく書いています。 #include<stdio.h> // func は int の要素数3の配列 {0, 1, 2} の中の n 番目の要素を返す int func(int (*pai)[3], int n){ // pai means 'p'ointer to 'a'rray of 'i'nt. return (*pai)[n]; } int main(){ int ai[] = {0, 1, 2}; // ai means array of int. int i = func(&ai, 1); // 同じことを、先頭要素のポインタを経由してやりたい int *pi = &ai[0]; // pi means pointer to int. int j = func((int*[3])pi, 1); // "(int の要素数3の配列) へのポインタ" を // 意味するキャストがしたい // (int*[3]) ではエラーになる printf("Of course %d = %d.\n", i, j); return 0; }

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

    よくネット上で、「配列名はポインタ」というような表現が出てきますが、 「配列名はポインタになっているのである。 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) となります。 そうすると、「配列名はポインタ」というような表現は間違っていることになり、誤解の元になるので、改めるべきではないかと思うのですが。

専門家に質問してみよう