
二次元配列、三次元配列のサイズが知りたいです。
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;
}
(注)インデントのため、全角空白を使っています。

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で違う定義になっているのならごめんなさい。

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を探す
今、見られている記事はコレ!
-
弁護士が語る「合法と違法を分けるオンラインカジノのシンプルな線引き」
「お金を賭けたら違法です」ーーこう答えたのは富士見坂法律事務所の井上義之弁護士。オンラインカジノが違法となるかどうかの基準は、このように非常にシンプルである。しかし2025年にはいって、違法賭博事件が相次...
-
釣りと密漁の違いは?知らなかったでは済まされない?事前にできることは?
知らなかったでは済まされないのが法律の世界であるが、全てを知ってから何かをするには少々手間がかかるし、最悪始めることすらできずに終わってしまうこともあり得る。教えてgooでも「釣りと密漁の境目はどこです...
-
カスハラとクレームの違いは?カスハラの法的責任は?企業がとるべき対応は?
東京都が、客からの迷惑行為などを称した「カスタマーハラスメント」、いわゆる「カスハラ」の防止を目的とした条例を、全国で初めて成立させた。条例に罰則はなく、2025年4月1日から施行される。 この動きは自治体...
-
なぜ批判コメントをするの?その心理と向き合い方をカウンセラーにきいた!
今や生活に必要不可欠となったインターネット。手軽に情報を得られるだけでなく、ネットを介したコミュニケーションも一般的となった。それと同時に顕在化しているのが、他者に対する辛らつな意見だ。ネットニュース...
-
大麻の使用罪がなかった理由や法改正での変更点、他国との違いを弁護士が解説
ドイツで2024年4月に大麻が合法化され、その2ヶ月後にサッカーEURO2024が行われた。その際、ドイツ警察は大会運営における治安維持の一つの方針として「アルコールを飲んでいるグループと、大麻を吸っているグループ...
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
関数での配列の扱いかたについ...
-
ナップサック問題について
-
C# Listを使わずに2次元配列の...
-
C言語で特定列だけを抽出して配...
-
c言語での文字列の渡し方
-
C言語で2次元配列の引数定義や...
-
データ構造について
-
C言語Char型配列に小数値を入れ...
-
配列のポインタ?
-
[EXCEL] INDEX関数の不具合?
-
C言語 配列の長さの上限
-
RPGの配列について
-
銀行ATMの数字キーの配列
-
関数の引数に括弧を使う理由
-
著作権はどこまで適用!?
-
配列の内容をファイルの書出す
-
LGノートPCグラムについて
-
多次元配列のデータ個数やメモ...
-
VB2005 文字型(String)について
-
関数に付いて
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語 配列の長さの上限
-
配列を使わずに、変数名を動的...
-
C# Listを使わずに2次元配列の...
-
【速いブラインドタッチ】手を...
-
配列をEraseしてもメモリが開放...
-
テキストファイルから文字列を...
-
先頭アドレスとは何ですか?
-
配列で格納したものをmsgboxで...
-
複数の選択範囲の行番号を個別...
-
C# 配列の変数宣言について。
-
C++ vectorに配列をプッシュしたい
-
配列を含む構造体の初期値について
-
VBで構造体の配列を関数に渡す...
-
C言語で特定列だけを抽出して配...
-
キーボードのキー配列について
-
ExcelVBAで質問です。離れた二...
-
2次元配列を戻り値とする関数?
-
unsigned char配列への入力の仕方
-
【C言語】配列の中に配列を入れ...
-
Redimした動的配列はEraseする...
おすすめ情報