質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

2回答

1119閲覧

C 言語: 配列とポインタ

ruby_11

総合スコア37

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2018/02/24 05:59

編集2018/02/24 07:00

C 言語の配列とポインタに関して質問です.

ポインタを使って配列の要素にアクセスする際には,
以下のように配列のアドレスをポインタ変数に代入する必要があることはわかります.

c

1int a[10] = {10, 20, 30}, *p; 2p = a;

しかし,多次元配列にアクセスする際には,データ型にキャストする必要があると参考書には書いてあります.

c

1float balance[10][5], *p; 2p = (float *)balance; 3*(p + (3 * 5) + 1)

キャストの必要性は,多次元配列と 1 次元配列の違いであることはなんとなく理解できるのですが,
どうしてキャストが必要なのかわかりません.

よろしくお願いします.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2018/02/24 06:56

p = int ではなく p = aと思われます。
ruby_11

2018/02/24 06:59

ご指摘ありがとうございます.p = a でした.
guest

回答2

0

質問への回答

キャストは変数型を一致させる為だけに必要です。
「エラーになる」か「警告が出るがコンパイルが通る」か「エラーも警告も無し」かはコンパイラ依存になると思います。

データ型にキャストする必要があると参考書には書いてあります.

つまりこれに対する回答は「必要がある」かどうかと言われると「必ずしも必要ではないケースもある」という回答になる気がします。
勿論あるべき姿は変数型の一致したプログラムです。
キャストして合わせるのも問題ありませんが(※1)、私個人としては配列の先頭メモリアドレスをpに代入するという意味でp = &a[0]p = &balance[0][0]という書き方が理解しやすい形かと思います。この書き方の場合型が一致しているのでキャストも不要です。

※1について

これらは全て同じ「配列の先頭メモリアドレス」をpに代入します。
p = balance, p = &balance, p = &balance[0], p = &balance[0][0]
どれも正しい「配列の先頭メモリアドレス」を代入している為プログラムは期待通りに動きます。
しかし、型が一致していない為コンパイル時にエラーまたは警告が出る場合があります。
この点を解決する為にキャストして型を「強制的に一致」させます。

Cのキャストは強制的なので場合によっては安全でない場合があります。
C++ではCのような何でも強制的にキャストできてしまうキャストは邪道とされてます。

余談

話が少しズレますが、実際のメモリと配列とポインタの関係については以下の画像を見て頂ければイメージが出来ると思います。

c

1int a[10] = {10, 20, 30}, *p; 2p = &a[0];

c

1float balance[10][5], *p; 2p = &balance[0][0];

投稿2018/02/24 07:33

編集2018/02/24 08:00
aglkjggg

総合スコア769

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

型の違いがあるからキャストが必要です。

上の例では、a は 「int 型の要素を 10 個持つ配列」です。単に a としたときは、「int 型の要素を 10 個持つ配列」の先頭要素のアドレス、つまり「int 型」へのアドレスとなります。

balance は、 「「float 型の要素を 5 個持つ配列」を 10 個持つ配列」です。単に balance としたときは、「「...」を 10 個持つ配列」の先頭要素のアドレス、つまり「float 型の要素を 5 個持つ配列」へのアドレスとなります。特別扱いはないということです。

balance をそのまま受け入れるようなポインタ型変数は、例えば次の ap のようなものです。

c

1float balance[10][5]; 2float (*ap)[5]; 3ap = balance;

ただし balance の先頭アドレスは型が違うだけで、実質先頭要素のアドレス &balance[0][0] と同じものになります。

投稿2018/02/24 06:40

Eki

総合スコア429

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ruby_11

2018/02/24 06:58 編集

コメントありがとうございます. a の場合は 1 次元であるため,配列の要素にアクセスするための段階が 1 回であり, 一方で balance の場合は,1 段階のアクセスでは 10 個の配列の先頭のアドレスを入手し, 2 段階目で,各配列の 5 つの要素にアクセスすることができるため,参照の際に再度キャストをする必要がある,という理解で正しいでしょうか?
Eki

2018/02/24 07:19 編集

1段階のアクセス ... `balance`, 10個の配列の先頭のアドレスを入手 2段階のアクセス ... `balance[0]`, 「10個の配列の 0 番目の要素 = 5個の配列」の先頭アドレスを入手 と理解しました (間違ってたらごめんなさい) 。それなら前半は正しいと思います。 キャストに関しては段階は関係はありません。3次元でも同様です。 ``` float balance[10][5][10]; float *p = (float*)balance; ``` キャストが必要となるのは本当に「ただ型が違うから」、です。質問文にある代入は、コンパイラには float (*)[5] という型の値を float * 型に代入するように見えます。普通、違う型に代入しようとするのは何か間違えている可能性が高いので、間違えてませんか?とコンパイラは警告ないしエラーを出すのです。 しかし、この場合、実質アドレスは同じのものになるとプログラマは知っていて、これを float * として扱いたいという意思があります。だから、「これは無理矢理 float * で扱いたいんです」ということを伝えるためにキャストを使うのです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問