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

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

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

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

4回答

17856閲覧

C++である関数の引数において多次元配列を受け取る

m0Rya

総合スコア37

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2015/11/07 14:36

C++で関数の引数において多次元配列を受け取って、その配列のサイズを取得しようとすると上手く行きません

具体的になにがどううまく行かないのかというと

lang

1#include <iostream> 2using namespace std; 3 4void func(int arr[][2]); 5 6int main(){ 7 int arr[2][2] = { 8 {1,2}, {3,4} 9 }; 10 11 func(arr); 12 return 0; 13} 14 15void func(int arr[][2]){ 16 cout << sizeof(arr) << endl; 17 cout << sizeof(arr[0]) << endl; 18}

上のコードを実行した時に、intのサイズが4であるとすると結果は、
16
8
と出力されるはずなのに
8
8
と出力されます

func関数内の
cout << sizeof(arr) << endl;
の部分においてちゃんとサイズが取得できていません。

配列のサイズを色々変えてみても
sizeof(arr[0])
の値は期待通りに出るのですが
sizeof(arr)は
上に同じく8と出力されてしまいます

また関数の引数を
void func(int(*arr)[2]);
void func(int arr[2][2]);
のように宣言しても同じ結果になりました。

多次元配列を引数で受け取ってその配列全体のサイズを取得する場合どのようにすればいいのでしょうか。

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

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

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

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

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

guest

回答4

0

残念ながら、C++の配列変数にはサイズ情報が保存されていないので、意図するコードはかけません。
C++の配列はCの頃からの仕様をそのまま受け継いでおり、「配列のサイズはプログラマーが管理しろ」というスタンスが引き継がれています。したがって、質問にあるコードのように引数の受け口を[]としている時点でその添え字のサイズの情報が失われています。C#やJavaのような今時の高級言語だと、変数自体にサイズ情報が保存されるので、arr.Lengthのような記述ができるのですけどね。

C++で配列のサイズ情報を一緒に管理するなら、STLのコンテナを使うのが手っ取り早いのですが、二次元配列を直接扱うものはないので、配列の配列という具合にやるか、いっそ自分で二次元配列クラスを作ってしまうか、どちらかになると思います。

あとは、質問者さんの意図に沿うかどうか判りませんが、テンプレートを使うことでサイズ情報を「コンパイラーに管理してもらう」ということならできます。

だいたいこんな感じ

C++

1// 配列のサイズが判る関数 2template <size_t N, size_t M> 3void func(int (&arr)[N][M]) 4{ 5 cout << N << endl; 6 cout << M << endl; 7}

func関数にはどのようなサイズの二次元配列でも渡せますし、例えば、int arr[10][20];と定義した変数をfunc関数に渡せば、func関数内ではN=10, M=20になります。
※元のコードにつられて*Mを余計に書いてしまったので削除しました。

投稿2015/11/07 15:20

編集2015/11/07 15:27
catsforepaw

総合スコア5938

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

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

0

多次元配列を引数で受け取ってその配列全体のサイズを取得する場合どのようにすればいいのでしょうか。

要素数が既知の多次元配列を関数引数として受け取りたい場合、(多次元)配列への参照型を利用できます。要素数[2][2]の2次元配列なら int (&)[2][2] 型となります。具体的なコードはChironianさん回答を参照ください。

void func(int(*arr)[2]);
void func(int arr[2][2]);
のように宣言しても同じ結果になりました。

(C言語から引き継いだ仕様として)C++言語では、関数引数として「配列型そのまま」を渡すことができません。結果的に、上記2つは全く同じ関数宣言と解釈されます。

関数呼び出し元の実引数(argument)では、自動的に「配列の先頭要素をさすポインタ型」へと変換されてしまいます。つまりmain()関数のローカル変数arrint [2][2]型は、関数の実引数としては&arr[0]int (*)[2]型へと暗黙変換されます。

呼び出される関数の仮引数(parameter)宣言では、配列型int [2][2]のように記述してもポインタ型int (*)[2]として解釈されます。このとき、配列の要素数(1個目の2)は無視されます。

投稿2015/11/08 02:29

yohhoy

総合スコア6189

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

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

0

まず main内の arr と、func内の仮パラメータ arr は別物である事に注意してください。
func内での arr は、mainから渡された arr では無く、func内の仮パラメータ arr[][2]への参照です。

また、配列を関数に渡す場合、配列そのものではなく配列のポインタが渡される事に注意してください。
この辺の仕組みは、C/C++ の関数コーリングシーケンスを参照してください。

最も簡単な方法は、
void func(int sz, int arr[][2]) 等のようにサイズを渡す。

もしくは、String の実装のように、class 等を使用してデータの一部に配列のサイズを含めるのが良いでしょう。

###あまりお勧めは出来ませんが(かなり危険な方法に思えます)、 ←左記部分は、勘違いでした
C++の参照で渡す方法もあります。

投稿2015/11/07 15:13

編集2015/11/07 16:06
T.Kanno

総合スコア915

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

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

Chironian

2015/11/07 15:29 編集

> また、配列を関数に渡す場合、配列そのものではなく配列のポインタが渡される事に注意してください。 void func(int arr[2][2]);で渡すとコピーが渡ると思ってました。 この形式でもポインタで渡るのですね。 どうも配列は特殊な扱いが多くて混乱します。 でも、また一つ勉強になりました。ありがとう。 あ、何故に参照で渡すのは危険なのでしょうか? ポインタ渡しとあまり変わらないと思いますよ。
T.Kanno

2015/11/07 16:08 編集

すみません、ちょっと誤解がありました。 参照/値渡しで、危険な臭いがしたのは、反対の値渡しの方でした。 単純に該当部分を消してしまうとこのスレッドが意味不明になってしまうので、 あえて、間違い部分は残します。(訂正線とかがほしいですね)
catsforepaw

2015/11/07 16:03

あいたたっ。私も似たような勘違いをしてました。気づかせてくださってありがとうございます。 コピーは走らないということは知っていましたが、最初の添え字を指定してもその情報は失われてしまうようですね。ちょっと確認しましたが、最初の添え字が違ってもコンパイルが通ってしまいました。
guest

0

こんばんは。

本当ですね!
void func(int arr[2][2]);で受けてもダメって驚きです。
検索したら、ここが見つかりました。

配列型の仮引数は、その配列の先頭要素へのポインタに型調整される。したがって、仮引数をオペランドとするsizeof式は、配列のサイズではなくポインタサイズを返す。ただし、仮引数が多次元配列であり、その部分配列がsizeofのオペランドである場合、その部分配列のサイズを返す。

だそうです!!
つまり、64ビットでビルトしているから、ポインタ・サイズの8バイトが表示されているようですね。

で、コピーではなく参照で渡すと渡せました。
配列の参照はここが参考になります。

C++

1#include <iostream> 2using namespace std; 3 4void func(int (&arr)[2][2]); 5 6int main(){ 7 int arr[2][2] = { 8 {1,2}, {3,4} 9 }; 10 11 func(arr); 12 return 0; 13} 14 15void func(int (&arr)[2][2]){ 16 cout << sizeof(arr) << endl; 17 cout << sizeof(arr[0]) << endl; 18}

投稿2015/11/07 15:04

Chironian

総合スコア23272

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問