![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?a65a0e2)
二次元配列、三次元配列のサイズが知りたいです。
unsigned char test1[100];
は sizeof( test1 )で100ですが、
unsigned char test1[100][30];
はどのように書いたら100または、30なのか知りたいです。
unsigned char test1[100][30][5];
これも100,30,5のサイズをそれぞれ得る方法が知りたいです。
因みに以下のような数値なし[]を見たことがあるのですが
どういう意味でしょうか?
unsigned char test1[][30];
サイズを理解していないのは、ポインタを理解していないと同じことでしょうか?今更ながら自分自身が不安です。
No.6ベストアンサー
- 回答日時:
> 普通は、こういうアクセスをするとメモリ破壊になるからやってはいけない
この理由と、もう一つ別の理由。
> printf("%d\n", sizeof ary / sizeof ary[100]);
> printf("%d\n", sizeof ary[100] / sizeof ary[100][30]);
> printf("%d\n", sizeof ary[100][30]) / sizeof ary[100][30][5];
は、他のサイズの配列の場合に使い回しが効かない、という弱点があります。
miraiyaの回答だと、こんな風にマクロ定義が可能です。
#include <stdio.h>
#define SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
int main(void)
{
unsigned char test1[100];
unsigned char test2[100][30];
unsigned char test3[100][30][5];
printf("%d\n", SIZE(test1));
putchar('\n');
printf("%d\n", SIZE(test2));
printf("%d\n", SIZE(test2[0]));
putchar('\n');
printf("%d\n", SIZE(test3));
printf("%d\n", SIZE(test3[0]));
printf("%d\n", SIZE(test3[0][0]));
return 0;
}
(注)インデントのため、全角空白を使っています。
![](http://oshiete.xgoo.jp/images/v2/common/profile/M/noimageicon_setting_04.png?a65a0e2)
No.14
- 回答日時:
まず、数値無しの [] ですが、これは、多くの場合に配列名が配列の先頭を指すポインタに変換されるという規則に関わっています。
配列の定義の場合は、配列全体のサイズを決定する必要があるので、要素のサイズは省略できません。
一方で、配列の宣言(のみ)の場合(関数の仮引数や、extern と伴って使われる場合ですね)実質的には、ポインタに変換されてしまいます。
例えば、test1[10] が、 *(test1 + 10) と等価であるのはご存じかと思います。
従って、配列の宣言の時には、配列のサイズがわからなくても、処理ができるので、配列のサイズは省略できます
逆に、関数の仮引数で配列のサイズを指定しても、それによる変化はありません。例えば、
void func(char test[100])
として、この間数内部で、sizeof(test) を参照すると、ポインタのサイズになっていることがわかります。
さらにいえばC言語では、多次元配列というのは存在しません。あくまでも、「配列の配列」です。
このため、char test1[100][30][5] は、char [30][5] の配列ということで、char test1[][][5] のような省略はできません。
(ただ単に、2番目以降のサイズがわからないと、次の要素に行く時にポインタをいくつ進めたらいいかわからないというだけのことですが、それは、とりもなおさず、「どのような型の配列なのかわからない」ということでもあります)
次に、話題がずれた方向にもう一つで申し訳ありませんが……
C言語(C++でも)の規格上、ポインタというのは、必ずしも、直接の、メモリ番地を意味しません。このために、ポインタの演算結果が、必ずしもポインタとして有効であるかどうかはわかないことになっています。
このために、
unsigned char test1[100];
に対して、test1[120]; に相当するもの ( *(test1 + 120) でアクセスできるはずのメモリ)が、存在するかどうかは保障されていません。
この範囲として保障されているのが、
test[-1] から test[100] までです。
ついでにいえば、 &test[-1] < &test[0] < .. < &test[99] < &test[100] になります。かならずしも、&test[100] < &test[101] が(ポインタの計算だけだったとしても)保障されていない点に注意が必要です。
これは、malloc() などで確保した領域に対しても同様で、
char *p = malloc(100); に対して、p[100] という場所はとりあえず存在しますが、p[101] という場所は、存在すらしないかも知れないということになります。
こういう訳で、配列の、要素ひとつ前後は、具体的に要素へのアクセスを行わなければ(そのアドレスをとるだけなら)OKということになっています。
No.13
- 回答日時:
質問者の意図からずれてしまっている様なので、話題を戻して。
>unsigned char test1[100][30];
>はどのように書いたら100または、30なのか知りたいです。
sizeof演算子は、ご存知のように渡されたオブジェクトのサイズを返すので、
sizeof test1 → 3000
sizeof test1[0] → 30
となります。
これは、test1というオブジェクトは2次元配列全体を指しますので合算になりますし、test1[0]はこの2次元配列中の先頭の配列要素(char型30個の1次元配列)を指すためです。
従って、質問に対する回答は、
sizeof test1[0] → 30
sizeof test1 / sizeof test1[0] → 100
となります。
ここで、sizeof test1[0]をsizeof test1[1]にしても結果は同じです。
(渡されたtest1[1]が2番目の配列要素になっただけでサイズは変わらないため)
因みに他の皆さんが議論されているのは、test1は0~99までの配列なので、sizeof test1[100]とした時に実際には存在しないオブジェクトと
言う観点からの議論です。(sizeof演算子は実際のメモリを参照するわけではないので、コンパイル時も実行時もエラーになることはないと思いますが。)
>unsigned char test1[100][30][5];
>これも100,30,5のサイズをそれぞれ得る方法が知りたいです。
同様に、
sizeof test1[0][0] → 5
sizeof test1[0] / sizeof test1[0][0] → 30
sizeof test1 / sizeof test1[0] → 100
が得られます。
No.12
- 回答日時:
#7で明確に[0]にしたほうが良いですねと訂正しているのに
そのあとでもそこを突っつくのは体のいいいじめですか?
ary[100]に関してですが、宣言時も100を使っていますので
K&R の以下の記述によれば通らないことはないと思いますがどうでしょうか?
The problem is that &tab[-1] and &tab[n] are both outside the limit of the array tab.
The former is strictlly illegal, and it is illegal to dereference the latter.
The language definition does guarantee, however, that pointer arithmetic
that involves the first element beyond the end of an array
(that is, &tab[n]) will work correctly.
(6.4 Pointers to Structures)
dereference するとダメよとはありますが、今回の件に関しては
関係ないし、記述すること自体が不法ということではないと思います
(dereferece = 評価ではありません)。
ISOとかJISで違う定義になっているのならごめんなさい。
![](http://oshiete.xgoo.jp/images/v2/common/profile/M/noimageicon_setting_16.png?a65a0e2)
No.11
- 回答日時:
配列の要素数を求めるための式の中に、要素数の値を含むのは
矛盾しているような気がします。
だからこそ、何人かのかたが書かれているように
「先頭要素のサイズ」で割る必要があると思います。
No.10
- 回答日時:
「破綻する」というのはちょっと言い過ぎでありました。
「保守性が下がる」あたりの方がよかったかなと思います。
定義した範囲外の要素にアクセスするコードを書くと、
将来そのコードを保守する人(将来の自分自身を含む)が見たときに
「何かの間違いではないか???」という疑問が渦巻くことを
心配してしまいます。
No.9
- 回答日時:
破綻するという意味では、sizeof ary[100]ではなく、%dにこそ注目すべきです。
sizeof演算子の評価結果はsize_t型であり、size_t型同士の除算結果はsize_t型またはint型になります。ここで、結果の型がint型かint型未満のサイズの型なら問題ありません(そうなる処理系はほとんどありません!)。あるいは、unsigned int型に定義されたsize_t型の場合も大抵大丈夫でしょう。しかし、size_t型の定義がunsigned long型などの場合、書式指定と実引数の型が矛盾するため、未定義の動作を引き起こします。
かといって、実引数をint型にキャストすればよいかというとそうでもありません。キャスト前の値がint型の表現範囲を超えていた場合、処理系定義の値になるか処理系定義のシグナルが発生します。結果として、期待した動作になるとは限らないわけです。(今回は15000以下の値しか扱わないので大丈夫ですが、32768以上を扱う場合は要注意です)
というわけで、こんなときはunsigned long型のような符号無し整数型で、かつ十分大きな型にキャストする方が望ましいと思います。具体的には、
printf("%lu\n", (unsigned long)(sizeof ary / sizeof ary[100]));
といった具合です。
もとの質問が処理系を特定しているのであれば、ここまで考慮する必要はないかもしれません。
ary[100]に関しては、今回はsizeof演算子のオペランドだったので問題ありません。また、&ary[100]とした場合も問題ありません。しかし、ary[100]という式を評価した場合の動作は未定義です。多くの処理系では、何事もなくコンパイルできてしまいますが、未定義の動作はエラー報告をすることを義務づけられていないので、(コンパイル時点で)既に誤動作していると考えた方が無難です。少なくとも、移植性はまったくありません。
No.8
- 回答日時:
★私もマクロ関数を作って利用していますね。
・回答者 No.6 さんのマクロ定義と同じです。→マクロ名は違いますが…。
#define ArrayOf(s) (sizeof(s) / sizeof((s)[0]))
と定義して使っています。
・使い方は『unsigned char test1[100][30][5];』の場合は
(1)ArrayOf(test1).........[100]の添え字の要素数を返す
(2)ArrayOf(test1[0])......[30]の添え字の要素数を返す
(3)ArrayOf(test1[0][0])...[5]の添え字の要素数を返す
・『ArrayOf()』マクロ関数はどんな型でも構造体などの配列でも添え字の要素数をコンパイル前に
自動的に計算してくれます。定義内容から『sizeof』演算子を使っています。
三次元配列の場合『ArrayOf(test1)』と指定すると[100]という添え字の要素数を計算しますが
これは sizeof(test1) / sizeof(test1[0]) を計算しています。
=sizeof(test1) / sizeof(test1[0])
=[100]x[30]x[5] / [30]x[5]
=15000 / 150
=100
・多次元配列の各要素数を計算したい場合は
ArrayOf(test1)
ArrayOf(test1[0])
ArrayOf(test1[0][0])
ArrayOf(test1[0][0][0]) ←三次元配列の場合、これはエラーですよ。注意!
などとします。
・『unsigned char test1[][30];』のような要素数を省略すると二次元の部分が『可変長』という
感じで宣言されます。つまり『test1[1][30]』~『test1[100][30]』など二次元の添え字部分は
固定にしないため場合によっては便利な記述です。
・例えば
static const char *string[] = {
"文字列1",
"文字列2",
:
"文字列n",
NULL,
};
という定義を見たことありませんか?
これと同じですよ。→任意要素数を定義している。
・ただし、この指定は一番大きい外側の次元部分のみしか許されません。
つまり、『test1[100][][30];』などはエラーです。『test1[][10][30];』としか記述できません。
最後に:
・回答者 No.1、No.3、No.7 さんのように添え字の数に『[0]』以外を指定しても正しくコンパイル
できます。また、動作にも悪影響はありません。でも『sizeof(ary[100][30][5]);』という指定方法は
誤解を招きやすいと思います。『破綻する』か、『破綻しない』とかの問題ではありません。
可読性です。ぱっと見て『間違い?』と見えるような記述はしない方が良いと言うことです。
・以上。おわり。
No.7
- 回答日時:
#1です。
徹夜寸前の状況で書いたんで不備があることは認めますが、
考えなしに100だの30だのの生の数字を書いたわけではありません。
[0]を使ったほうが良いことはその通りですし、議論を巻き起こすことは
本意でないしここではご法度ですので詳細は書きません。
ですが、
> 定義外の添字を使っているために破綻します。
破綻とはどのような意図で書かれていますか?
正しい答えが求まらないとか、コンパイルエラーになるとかいうことでしょうか。
もしそのようなコンパイラがあるというなら教えていただけませんか?
一応コンパイルして結果を確かめてから書いていますから。
もちろんあるコンパイラで通るからといってそれが問題ないという
ことではないということはわかりますが、「破綻する」とまで云われては
文句のひとつもつけたくなります。
No.5
- 回答日時:
配列の要素数は「(配列全体のサイズ)/(1要素のサイズ)」ですから、
「sizeof(a)/sizeof(a[0])」でも「sizeof(a)/sizeof(a[1])」でも
はたまた「sizeof(a)/sizeof(a[100])」でも計算できるのは確かです。
sizeof 自体はコンパイル時に解決されますから実行時にエラーになる
ということもありませんし。
ただし「sizeof(a)/sizeof(a[100])」などと書こうものなら、後で
そのコードを読んだ人を混乱におとしいれること間違いなしです
(書いた人自身も含めて)。
定石どおり素直に「sizeof(a)/sizeof(a[0])」と書きましょう。
多次元配列の場合はNo.2の方が示されているとおりに。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語 共用体について コマンドライン引数で値を2つ入力したときに、argv[2]の値をUNI u1 4 2022/04/25 20:34
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
- PHP $_SESSIONについて教えて下さい。 2 2023/03/02 09:18
- SQL Server [SQLServer] テーブル名からカラム名を取得する 1 2022/08/23 21:20
- C言語・C++・C# const char** p;のとき、free(p)でC4090エラーとなるのはなぜですか 3 2023/03/31 16:28
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# c言語 配列とポインタについて 3 2023/02/09 22:53
- C言語・C++・C# C言語について コマンドラインで >変数 12.00 (char型) と、小数点付きの値を共用体に渡 1 2022/04/22 16:56
- Visual Basic(VBA) Excle VBA Findメソッドについて 3 2022/07/15 13:56
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・一番好きなみそ汁の具材は?
- ・泣きながら食べたご飯の思い出
- ・「これはヤバかったな」という遅刻エピソード
- ・初めて自分の家と他人の家が違う、と意識した時
- ・いちばん失敗した人決定戦
- ・思い出すきっかけは 音楽?におい?景色?
- ・あなたなりのストレス発散方法を教えてください!
- ・もし10億円当たったら何に使いますか?
- ・何回やってもうまくいかないことは?
- ・今年はじめたいことは?
- ・あなたの人生で一番ピンチに陥った瞬間は?
- ・初めて見た映画を教えてください!
- ・今の日本に期待することはなんですか?
- ・集中するためにやっていること
- ・テレビやラジオに出たことがある人、いますか?
- ・【お題】斜め上を行くスキー場にありがちなこと
- ・人生でいちばんスベッた瞬間
- ・コーピングについて教えてください
- ・あなたの「プチ贅沢」はなんですか?
- ・コンビニでおにぎりを買うときのスタメンはどの具?
- ・おすすめの美術館・博物館、教えてください!
- ・【お題】大変な警告
- ・洋服何着持ってますか?
- ・みんなの【マイ・ベスト積読2024】を教えてください。
- ・「これいらなくない?」という慣習、教えてください
- ・今から楽しみな予定はありますか?
- ・AIツールの活用方法を教えて
- ・最強の防寒、あったか術を教えてください!
- ・歳とったな〜〜と思ったことは?
- ・モテ期を経験した方いらっしゃいますか?
- ・好きな人を振り向かせるためにしたこと
- ・スマホに会話を聞かれているな!?と思ったことありますか?
- ・それもChatGPT!?と驚いた使用方法を教えてください
- ・見学に行くとしたら【天国】と【地獄】どっち?
- ・これまでで一番「情けなかったとき」はいつですか?
- ・この人頭いいなと思ったエピソード
- ・あなたの「必」の書き順を教えてください
- ・14歳の自分に衝撃の事実を告げてください
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語 配列の長さの上限
-
銀行ATMの数字キーの配列
-
C# Listを使わずに2次元配列の...
-
配列の参照渡しで型が一致しま...
-
【C言語】配列の中に配列を入れ...
-
先頭アドレスとは何ですか?
-
C# 配列の変数宣言について。
-
配列を使わずに、変数名を動的...
-
配列で格納したものをmsgboxで...
-
C言語初心者 ポインタについて...
-
配列を含む構造体の初期値について
-
複数の選択範囲の行番号を個別...
-
配列をEraseしてもメモリが開放...
-
ヘッダーファイルからの取り込...
-
Visual C++で配列を動的に確保...
-
vectorで文字列の配列を使う方法
-
2次元配列を戻り値とする関数?
-
intel fortranコンパイラについて
-
多次元配列のポインタ渡し
-
LGノートPCグラムについて
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語 配列の長さの上限
-
配列を使わずに、変数名を動的...
-
配列で格納したものをmsgboxで...
-
テキストファイルから文字列を...
-
C# 配列の変数宣言について。
-
C# Listを使わずに2次元配列の...
-
先頭アドレスとは何ですか?
-
C言語で特定列だけを抽出して配...
-
【速いブラインドタッチ】手を...
-
配列の参照渡しで型が一致しま...
-
unsigned char配列への入力の仕方
-
ExcelVBAで質問です。離れた二...
-
VBで構造体の配列を関数に渡す...
-
複数の選択範囲の行番号を個別...
-
C++ vectorに配列をプッシュしたい
-
【C言語】配列の中に配列を入れ...
-
配列をEraseしてもメモリが開放...
-
Excel、VBAのユーザーフォーム...
-
Redimした動的配列はEraseする...
-
VB.NET 構造体の配列の検索機能...
おすすめ情報