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

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

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

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

Q&A

解決済

3回答

525閲覧

C言語の関数において配列を引数として使う場合について

Suika01

総合スコア12

C

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

0グッド

0クリップ

投稿2022/02/17 10:14

質問

参考書に記載の以下のコードに関連していくつか質問があります。

[1]
C言語におけるポインタとは、メモリ上のアドレスを格納するための変数という認識で正しいでしょうか?

[2]
メモリ上のアドレスを代入できる変数はポインタ以外に存在するでしょうか?

[3]
コード20行目「ans = heikin(test);」の"test"は、変数test[0]のメモリ上のアドレスを格納したポインタとして働くと考えてよろしいでしょうか?
(ただし"test"には他の変数のアドレスを代入することはできない)

[4]
28行目「double heikin(int t[])」の"int t[]"は見た通り配列を宣言しているという認識で正しいでしょうか?
また、その場合"t[]"はいったい何を示しているのでしょうか?
20行目から引数としてアドレスが渡される(参照渡し)とすると、"t[]"はポインタ(="t")と考えるのが適切な気がしますが、配列名であらわされるポインタには他のアドレスを代入することはできないと学んだので、いまいち理解に苦しんでいます。

分かりにくい文面で恐縮ですが、回答よろしくお願いします。

該当のソースコード

C言語

1#include <stdio.h> 2 3// heikin関数の宣言 4double heikin(int t[]); 5 6// main関数 7int main(void) 8{ 9 int test[5]; 10 int i; 11 double ans; 12 13 printf("5人のテストの点数を入力してください。\n"); 14 15 for (i = 0; i < 5; i++) 16 { 17 scanf("%d", &test[i]); 18 } 19 20 ans = heikin(test); 21 22 printf("5人の平均点は%f点です。\n", ans); 23 24 return 0; 25} 26 27// heikin関数 28double heikin(int t[]) 29{ 30 int i; 31 double sum = 0.0; 32 33 for (i = 0; i < 5; i++) 34 { 35 sum += t[i]; 36 } 37 38 return sum / 5; 39}

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

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

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

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

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

guest

回答3

0

ベストアンサー

1

ポインタ型の値 (つまりはアドレスですが) をポインタと呼ぶ用語法が多いと思いますが、型や変数のことを指してポインタと言っている場合はあります。

2

整数型にアドレスをいれてもよいですが言語仕様として動作が保証されている範囲はかなり小さく、正しく扱うのが難しいので普通は避けるべきです。

3

test は配列 (この場合の型は int[5]) ですが、式中に現れる配列は単項 &sizeof のオペランドになるときを除いて先頭要素を指すポインタへと変換される規則になっています。 (つまりこの場合は int* へと変換される。)

変数 test[0] のメモリ上のアドレスを格納したポインタ

この場合は格納という言葉を使うとちょっと語弊がありますね。 C ではどこかのメモリ上に入っている値と一時的な値というのは区別されていて、この場合はメモリ上のアドレスであるような「一時的な値」です。

lvalue や rvalue という概念で区別されているので詳細はこれらの語で調べてみるとよいでしょう。 日本語訳 (JIS) では左辺値・右辺値という言葉が当てられているんですが、必ずしも演算子の左に現れるからとか右に現れるからということではなく固有の定義があるので気を付けてください。

4

いいえ。 仮引数に現れる配列の記法はポインタ型に調整される規則になっています。

つまりこの場合でいえば double heikin(int *t) と書いたのと同じ意味になります。

投稿2022/02/17 10:44

SaitoAtsushi

総合スコア5437

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

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

Suika01

2022/02/24 05:57

回答ありがとうございます。 なんとなくポインタと配列の関係が掴めてきました。
guest

0

ここから。
4. 「double heikin(int t[])」の"int t[]"は見た通り配列を宣言している?

まずお伝えしたいこと。関数定義の書き方で、次の2つは完全に同じです。
double heikin(int t[]) { ... }
double heikin(int *t) { ... }

仮引数の t[]*t と同じ意味です。ここの、この2つを、コンパイラは区別しません。つまり「int t[] 」の「t」は、呼出し元の(普通は)配列を指すポインタ変数です。

「int t[]」だと「t は配列を指している」というイメージ、ポインタ(「int *t」)だとメモリの一か所を指すイメージ・・・なんですが、ポインタが指している番地の隣にもメモリは連続して在るのだから、ポインタが一点(単独の変数)を指しているか、配列を指しているか、に何の違いもありません。
プログラムを書く人にとっても、どちらでも同じです。私は配列を指すイメージを強調したい時「int t[]」と書くことがあります。


1. はい。ポインタの値はメモリアドレスです。

2. 値としてのメモリアドレスは整数値です。浮動小数点数(float, double)ではない、のでポインタ値を整数型変数に代入できる場合があります。

経験上、大抵 unsigned long 型の変数には代入できるかな・・・ですが絶対大丈夫とは言えません。移植性は無いものと覚悟してください。つまり、ある環境(計算機とCコンパイラの組合せ)でうまくいっても、別の環境ではうまくいかないことがある、という事。
ポインタを代入する先はポインタ変数に限るのが身のためです。

3. 配列名は「配列の先頭アドレス」と読み換える、のが基本原則です(例外もある)。
ans = heikin(test); の"test"は「配列 test の先頭アドレス(という値)」
なので、配列testの先頭アドレスを関数に渡しています。
念のため: test&test[0] と書いても同じ。値も同じ。

この時、アドレスがひとつだけheikin()関数に渡ることは、関数側の書き方
double heikin(int t[]) { ... }
double heikin(int *t) { ... }
どちらでも「アドレス(ポインタ)をひとつ受け取る」ことに対応します。

ポインタ変数を使ったアクセスの書き方に2通りあることも確認しておきます。

C

1#include <stdio.h> 2int main(void) { 3 int arr[6] = { 2, 3, 5, 8, 13, 21 }; 4 int *ip; 5 ip = arr; // ip は arr 配列をポイントする 6 printf("%d, %d, %d, %d\n", ip[2], *(ip + 2), *(arr + 2), arr[2]); 7}

表示結果 5, 5, 5, 5 からわかるように

  • ip[2]
  • *(ip + 2)
  • *(arr + 2)

はいずれも arr[2] をアクセスします。

  • *(ip + 2) のように、いかにもポインタらしくアクセスできる
  • ip[2] のように、あたかも配列のようにアクセスできる

ポインタ変数を使って2通りの書き方ができます。

配列を丸ごと(=配列内のデータ全部を)関数に渡すことはできないが、配列の先頭アドレスを関数に渡せばよいじゃないか、だって先頭アドレスを受け取れば関数は配列の全てのデータにアクセスできるのだから・・・これが配列を関数の引数にするココロです。

P.S.
int test[5]; とした所で test = XXXX; はできません。いったん割り当てられた配列を、これだけで動かせたら大変なことです(笑)。
でも double heikin(int t[]) 関数のなかで t = XXXX; は普通にできます。 t はポインタ変数ですから、こちらは書き換え可能です。

投稿2022/02/18 23:35

rubato6809

総合スコア1380

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

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

Suika01

2022/02/24 05:56

回答ありがとうございます!
guest

0

C言語におけるポインタとは、メモリ上のアドレスを格納するための変数という認識で正しいでしょうか

YES. データはすべてメモリってところに配置されます。でも変数はそこから丸ごと取り出して変数なるものに入れて使っています。int型一個とかならいいですけど、構造体や要素数の大きい配列とかの場合、面倒です。

さらに関数に渡すとなると、変数や配列だと呼び出し側(main関数等)にあるデータと呼び出される関数内のデータ、つまり単純計算で二倍のデータがメモリ上に配置されることになります。メモリは有限ですから、できるだけ症比率を抑えたい。ってことで、「メモリのどこに配置されているか」っていう情報(= アドレス)を用います。これがポインタ。たとえばmain関数内では実際のデータを配列とかで保持して、関数に渡すときにはポインタで渡す。
呼び出された関数はポインタが指し示す場所(アドレス)を参照してそこにあるデータを使う。
そうすれば、 main関数内にあるデータ分 + ポインタのサイズ で済みます。(サイズのデカい配列や構造体等で体感できる)

メモリ上のアドレスを代入できる変数はポインタ以外に存在するでしょうか

無いです。C++だと 参照型ってのがありますが、C言語なので除外。

コード20行目「ans = heikin(test);」の"test"は、変数test[0]のメモリ上のアドレスを格納したポインタとして働くと考えてよろしいでしょうか

YES.

28行目「double heikin(int t[])」の"int t[]"は見た通り配列を宣言しているという認識で正しいでしょうか?

多分。厳密にはポインタですが、int *t だと int型データ一個分のことなのか 配列へのポインタって意味なのか不明瞭なので二つの書き方があるんだと思います。(関数名から推測できることはできるが、場合によっては…)

また、その場合"t[]"はいったい何を示しているのでしょうか

上で述べたように、int型の配列ってことを表していると思われる。

投稿2022/02/17 10:31

BeatStar

総合スコア4958

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

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

Suika01

2022/02/24 05:56

回答ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問