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

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

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

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

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Q&A

解決済

3回答

3622閲覧

C言語でmain関数から自作関数を呼び出して、呼び出した先でsizeof演算子で配列のサイズを取得しようとするが出来ない

tada_tadaa

総合スコア110

C

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

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

0グッド

0クリップ

投稿2016/09/20 12:57

お世話になっております。
C言語で、sizeof演算子を使って配列の要素数を取得したいのですが、main関数
内ではうまくいくのですが、main関数から配列と一緒に自作関数を呼び出して
呼び出した関数内で、配列の要素数を取得しようとしてもうまくいきません。
下にソースを載せます。

c

1#include <stdio.h> 2 3int max(int numnum[10]); 4 5int main(){ 6 int num[10] = { 23,42,15,32,52,63,32,43,11,34}; 7 8 /* 10が出力されてほしいが1が出力される */ 9 printf("値は %d です\n", max(num)); 10 11 /* これはうまく配列の要素数が取得できてる */ 12 printf("%d\n", sizeof num / sizeof num[0]); 13 14 return 0; 15} 16 17int max(int numnum[10]){ 18 int num_size = (sizeof numnum / sizeof numnum[0]); 19 int i; 20 for(i=0; i<10; i++) printf("配列 %d\n", numnum[i]); 21 22 return num_size; 23}

実行結果は

配列 23
配列 42
配列 15
配列 32
配列 52
配列 63
配列 32
配列 43
配列 11
配列 34
値は 1 です
10

となります。僕の予想では

値は 1 です

の部分が

値は 10 です

になると思ってたのですが、なりません。
自作関数を呼び出すときに、ポインタをつければいいのかと思って適当に
あれこれしてみましたが駄目でした。
どなたか原因が思い当たる方は教えて頂ければと思います。
よろしくお願いします。

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

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

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

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

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

guest

回答3

0

int max(int numnum[10]){

仮引数で、配列自体の宣言は出来ません。エラーにはなりませんが、int numnum[]つまりint *numnumと同じになります。なので、sizeof numnum / sizeof numnum[0]は、ポインタのサイズをintのサイズで割ることになります。この場合は結果が1ということなので、どちらも4バイト(8バイトと言うこともあり得る)だったのでしょう。

配列の処理を行う関数では、HogeAnimalLoverさんの回答にある通り、サイズを呼び出し側から渡すのが普通です。
例えば、配列のソートを行うqsort関数では、プロトタイプはこうなっており、

C

1void qsort(void *base, size_t nmemb, size_t size, 2 int(*compar)(const void *, const void * ));

こんな感じで呼び出します。

C

1int foo[100]; 23qsort(foo, sizeof foo/sizeof foo[0], sizeof foo[0], my_compare_func);

また、charの配列を文字列として扱う関数では、明示的にサイズを渡さず、'\0'が出てきたところで配列(文字列)の終わりと見なして、そこで処理をやめます。

投稿2016/09/20 13:39

otn

総合スコア84423

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

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

tada_tadaa

2016/09/20 14:30

回答ありがとうございます。 >ポインタのサイズをintのサイズで割ることになります。 具体的な内部の動作を教えていただき参考になります。 また、ほかの回答者様が言っておられるように、あらかじめ要素数を調べておき、その要素数を関数の引数として渡す方法や、#defineを使う方法、番兵を使う方法などがあるということが分かりました。案外泥臭い方法を使わないといけないんだなぁと思いましたが、これらの方法でプログラムを記述することにしました。 どうもありがとうございました。
otn

2016/09/20 14:35

> 案外泥臭い方法 Cならではと言うところですね。
guest

0

ベストアンサー

こんにちは。

C言語は、配列の変数名はその配列先頭へのポインタとなってしまいます。この時点で要素数情報が消えてしまいます。それを関数に渡しているため、関数に要素数が渡りません。
関数の仮引数定義で要素数を書いてもだめというのは衝撃的とは思いますが、それが仕様なのです。

対策は、面倒ですが、配列の要素数を配列と一緒にハンドリングするしかないです。
HogeAnimalLoverさんが言うとおり自作関数のパラメータを1つ増やして要素数を渡したり、#define ARRAY_SIZE 10などとしてハンドリングしたりすることも多いですね。「終了記号」については「番兵 wikipedia」で検索すると良いです。

もし、C++でも良いのでしたら、強力に拡張されているので取り扱いが格段に楽になってますよ。

投稿2016/09/20 13:35

Chironian

総合スコア23272

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

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

tada_tadaa

2016/09/20 14:17

回答ありがとうございます。 >それが仕様なのです。 自作関数のパラメータを1つ増やして要素数を渡したり、とは結構泥臭い方法をとらないと駄目なんですね。「終了記号」の意味についても分かりました。こちらのほうもやはり泥臭いというかなんというか。 >もし、C++でも良いのでしたら、強力に拡張されているので取り扱いが格段に楽になってますよ。 早くC++を学習したいです。今はまだC言語の学習中です。 どうもありがとうございました。
Chironian

2016/09/20 16:16

> 結構泥臭い方法をとらないと駄目なんですね。 ですね。C言語が高級アセンブラとも呼ばれる所以です。 > 早くC++を学習したいです。今はまだC言語の学習中です。 C言語は高級アセンブラとも呼ばれるだけあって、コンピュータの基本的な仕組みを学ぶにはたいへん有効です。そして、基本をしっかり理解していると非常に強いです。頑張って下さい。
tada_tadaa

2016/09/21 01:06

>基本をしっかり理解していると非常に強いです。 そうなんですよ。僕もそれを狙ってC言語を勉強しているわけです。 C言語からC++へ、そして他の言語へと計画中です。 >頑張って下さい。 ありがとうございます!
guest

0

『「sizeof 配列変数名/ (要素当たりのバイト数)」で配列要素数を取得できる』というのは条件があります。この例では分子は「ポインタ変数としてのバイト数」を指し示しております。

このような計算をするのであれば、「引数として配列要素数を渡す」、「終了記号を定めておく」などが必要です。

投稿2016/09/20 13:03

HogeAnimalLover

総合スコア4830

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

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

tada_tadaa

2016/09/20 13:15

回答ありがとうございます。 ところで、 >「引数として配列要素数を渡す」 というのがよく分からないのですが、これは例えば、関数を呼び出す前に配列要素数をすでに調べておいて、その調べた配列要素数を引数として自作関数に渡すということでよろしいのでしょうか?あと、 >「終了記号を定めておく」 というのがよく分かりません。具体的にはどういったことでしょうか? 質問ばかりで申し訳ありません。
HogeAnimalLover

2016/09/20 13:36

本例であれば int max(int numnum[], int length); というように配列長を示す引数を与えることが一つです。 または、「負の値は通常使わないものとする」とルールを定め、「負の値を配列の終了記号にする」という方法も考えられます。(文字列で言うところの「\0」記号を決めておく)
tada_tadaa

2016/09/20 14:01

コメントありがとうございます。 「引数として配列要素数を渡す」「終了記号を定めておく」の意味が分かりました。なんていうか結構泥臭い方法をとらないといけないんですね。 どうもありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問