Cで配列のサイズを確認するにはどうすればよいですか?


回答:


1260

エグゼクティブサマリー:

int a[17];
size_t n = sizeof(a)/sizeof(a[0]);

完全な答え:

配列のサイズをバイト単位で決定するには、sizeof 演算子を使用できます。

int a[17];
size_t n = sizeof(a);

私のコンピューターでは、intは4バイト長なので、nは68です。

配列の要素数を決定するには、配列の合計サイズを配列要素のサイズで除算します。次のように、タイプを使用してこれを行うことができます。

int a[17];
size_t n = sizeof(a) / sizeof(int);

正しい答え(68/4 = 17)を取得しますが、タイプがa変更された場合、 変更するのを忘れると厄介なバグが発生します。sizeof(int)

したがって、推奨される除数は、配列の最初の要素のサイズsizeof(a[0])または同等のものsizeof(*a)です。

int a[17];
size_t n = sizeof(a) / sizeof(a[0]);

もう1つの利点は、マクロで配列名を簡単にパラメーター化して取得できることです。

#define NELEMS(x)  (sizeof(x) / sizeof((x)[0]))

int a[17];
size_t n = NELEMS(a);

6
コンパイラーはコンパイル時に* int_arrのタイプ(したがって、sizeof(* int_arr)の値)を知っているため、生成されたコードは同じになります。これは定数であり、コンパイラーはそれに応じて最適化できます。
マークハリソン

10
sizeofの結果はコンパイル時の定数として定義されるため、すべてのコンパイラに当てはまります。
マークハリソン

451
重要:ここを読むのをやめないで、次の答えを読んでください!これはスタック上の配列でのみ機能します。たとえば、malloc()を使用している場合や、関数パラメーターにアクセスしている場合は、うまくいきません。下記参照。
Markus

7
CまたはC ++でのWindows APIプログラミングの場合、ARRAYSIZEmakroが以下で定義されています。WinNT.h(他のヘッダーによってます。したがって、WinAPIユーザーは自分のマクロを定義する必要はありません。
ルミ2014

17
@Markus配列型を持つすべての変数に対して機能します。これは「スタック上」である必要はありません。例えばstatic int a[20];。ただし、コメントは、配列とポインタの違いを理解していない読者に役立ちます。
MM

808

sizeofやり方は正しい方法であるIFFあなたがパラメータとして受け取っていない配列を扱っています。関数へのパラメーターとして送信された配列はポインターとして扱われるためsizeof、配列ではなくポインターのサイズを返します。

したがって、関数内ではこのメソッドは機能しません。代わりに、常にsize_t size配列の要素数を示す追加のパラメーターを渡します。

テスト:

#include <stdio.h>
#include <stdlib.h>

void printSizeOf(int intArray[]);
void printLength(int intArray[]);

int main(int argc, char* argv[])
{
    int array[] = { 0, 1, 2, 3, 4, 5, 6 };

    printf("sizeof of array: %d\n", (int) sizeof(array));
    printSizeOf(array);

    printf("Length of array: %d\n", (int)( sizeof(array) / sizeof(array[0]) ));
    printLength(array);
}

void printSizeOf(int intArray[])
{
    printf("sizeof of parameter: %d\n", (int) sizeof(intArray));
}

void printLength(int intArray[])
{
    printf("Length of parameter: %d\n", (int)( sizeof(intArray) / sizeof(intArray[0]) ));
}

出力(64ビットLinux OSの場合):

sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2

出力(32ビットWindows OSの場合):

sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1

10
なぜlength of parameter:2最初の配列要素へのポインタだけが渡されるのですか?
Bbvarghe 2013

16
@Bbvarghe 64ビットシステムのポインタは8バイト(sizeof(intArray))ですが、intは(通常)4バイト長です(sizeof(intArray [0]))。
Elideb 2013

13
@Pacerier:正しいコードはありません-通常の解決策は、配列と一緒に長さを個別の引数として渡すことです。
Jean Hominal 2013年

10
ちょっと待って、ポインタから配列に直接アクセスしてそのサイズを確認する方法はありませんか?Cはここが初めてです。
sudo

7
@Michael Trouw:気分が良くなる場合は、演算子構文を使用できます(sizeof array / sizeof *array)
chqrlie

134

sizeofポインタに減衰した配列値を処理する場合、これは役に立たないことに注意する必要があります。それが配列の先頭を指している場合でも、コンパイラにとっては、その配列の単一の要素へのポインタと同じです。ポインタは、それを初期化するために使用された配列について他に何も「記憶」しません。

int a[10];
int* p = a;

assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));

1
@ Magnus:標準では、sizeofはオブジェクトのバイト数を生成するものとして定義されており、そのsizeof(char)は常に1です。バイト内のビット数は実装固有です。編集:ANSI C ++標準セクション5.3.3 Sizeof: "sizeof演算子は、そのオペランドのオブジェクト表現のバイト数を生成します。[...] sizeof(char)、sizeof(signed char)およびsizeof(unsigned char)は、 1;他の基本型に適用されたsizeofの結果は、実装によって定義されます。」
Skizz

セクション1.6 C ++メモリーモデル:「C ++メモリーモデルの基本的なストレージユニットはバイトです。バイトは、少なくとも基本実行文字セットのメンバーを含めるのに十分な大きさであり、連続するビットのシーケンスで構成される数そのうち、実装によって定義されます。」
Skizz

2
CRAYにはchar32ビットのCがあったことを覚えています。すべての標準では、0〜127の整数値を表すことができ、その範囲は少なくとも-127〜127(charは符号付き)または0〜255(charは符号なし)のいずれかです。
フォンブランド2013

5
これは素晴らしい反応です。上記のアサーションはすべてTRUEと評価されることをコメントしておきます。
Javad

49

「トリック」のサイズは、私が知る最良の方法です。括弧の使用法は1つ小さいですが(私にとっては、これは大きな不満です)、重要な変更点があります。

ウィキペディアのエントリで明らかになっているように、C sizeofは関数ではありません。それは演算子です。したがって、引数が型名でない限り、引数を括弧で囲む必要はありません。これにより、引数がキャスト式のようになり、括弧も使用されるため、覚えやすくなります。

だから:あなたが以下を持っている場合:

int myArray[10];

あなたはこのようなコードで要素の数を見つけることができます:

size_t n = sizeof myArray / sizeof *myArray;

それは、私にとって、括弧付きの代替案よりもはるかに読みやすいです。また、インデックスよりも簡潔であるため、部門の右側の部分にアスタリスクを使用することをお勧めします。

もちろん、これもすべてコンパイル時間なので、除算がプログラムのパフォーマンスに影響することを心配する必要はありません。したがって、このフォームは可能な限り使用してください。

タイプではなく、実際のオブジェクトがある場合は、実際のオブジェクトでsizeofを使用するのが常に最善です。エラーが発生したり、間違ったタイプを指定したりする必要がないためです。

たとえば、ネットワークを介して、一部のデータをバイトストリームとして出力する関数があるとします。関数を呼び出して、send()送信するオブジェクトへのポインタとオブジェクトのバイト数を引数として受け取るようにします。したがって、プロトタイプは次のようになります。

void send(const void *object, size_t size);

そして、整数を送信する必要があるので、次のようにコーディングします。

int foo = 4711;
send(&foo, sizeof (int));

これでfoo、2か所でタイプを指定することにより、足で自分自身を撮影する微妙な方法を導入しました。一方が変更されても他方が変更されない場合、コードは壊れます。したがって、常に次のようにします。

send(&foo, sizeof foo);

今、あなたは保護されています。確かに、変数の名前を複製しますが、変更すると、コンパイラが検出できる方法で壊れる可能性が高くなります。


ところで、それらはプロセッサレベルで同じ命令ですか?sizeof(int)より少ない指示が必要sizeof(foo)ですか?
Pacerier 2013

@Pacerier:いいえ、同じです。int x = 1+1;対について考えてくださいint x = (1+1);。ここで、括弧は純粋に完全に美的です。
ケツァルコアトル2013年

@Aidiakapi事実ではありません。C99VLAを検討してください。
2016年

@unwindおかげで、私は正直に立っています。私のコメントを訂正するために、sizeofC ++およびC89では常に一定です。C99の可変長配列では、実行時に評価できます。
アイディアカピ2016年

2
sizeof演算子かもしれませんが、Linus Torvaldsによると関数として扱う必要があります。同意する。彼の有理をここで読んでください:lkml.org/lkml/2012/7/11/103
Dr. Person Person II

37
int size = (&arr)[1] - arr;

説明については、このリンクをチェックしてください


7
小さなnitpick:ポインター減算の結果にはtypeがありptrdiff_tます。(通常、64ビットシステムでは、これはより大きいタイプになりますint)。このコードでに変更intptrdiff_tた場合でもarr、アドレス空間の半分以上を占有する場合はバグが残っています。
2014年

2
@MMもう1つの小さなヒント:システムアーキテクチャによっては、アドレス空間はほとんどのシステムのポインタサイズほど大きくありません。たとえば、Windowsは64ビットアプリケーションのアドレス空間を8TBまたは44ビットに制限しています。したがって、たとえば4.1TBのアドレススペースの半分よりも大きいアレイがある場合でも、バグにはなりません。これらのシステムでアドレス空間が63ビットを超える場合にのみ、そのようなバグが発生する可能性さえあります。一般に、心配する必要はありません。
Aidiakapi

1
32ビットx86 Linuxまたは/3Gオプション付きWindowsの@Aidiakapiでは、3G / 1Gユーザー/カーネル分割があり、アドレススペースサイズの最大75%までのアレイサイズを使用できます。
ルスラン

1
foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];グローバル変数と考えてください。 定数ではないため、buf3[]宣言は失敗し(&buf1)[1] - buf1ます。
chux-モニカを

2
これは技術的に未定義の動作です。これは、標準では明示的に配列の最後を越えて逆参照することを禁止しているためです(格納された値を読み取ろうとしなかった場合でも)
MM


21

配列のデータ型がわかっている場合は、次のようなものを使用できます。

int arr[] = {23, 12, 423, 43, 21, 43, 65, 76, 22};

int noofele = sizeof(arr)/sizeof(int);

または、配列のデータ型がわからない場合は、次のようなものを使用できます。

noofele = sizeof(arr)/sizeof(arr[0]);

注:これは、配列が実行時に定義されておらず(mallocなど)、配列が関数に渡されない場合にのみ機能します。どちらの場合も、arr(配列名)はポインターです。


4
int noofele = sizeof(arr)/sizeof(int);コーディングよりも半分ほど優れていint noofele = 9;ます。を使用sizeof(arr)すると、配列のサイズが変化しても柔軟性が維持されます。ただしsizeof(int)arr[]変更の種類に応じて更新が必要です。sizeof(arr)/sizeof(arr[0])タイプがよく知られている場合でも使用することをお勧めします。使用した理由は不明intのためnoofelesize_t、タイプがで返されますsizeof()
chux-モニカを2016年

19

ARRAYELEMENTCOUNT(x)誰もが利用しているマクロは正しく評価されません。これは現実的には敏感な問題です。「配列」型になる式を使用することはできないためです。

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))

ARRAYELEMENTCOUNT(p + 1);

実際には次のように評価されます:

(sizeof (p + 1) / sizeof (p + 1[0]));

一方

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])

ARRAYELEMENTCOUNT(p + 1);

それは正しく評価されます:

(sizeof (p + 1) / sizeof (p + 1)[0]);

これは、配列のサイズと明示的には関係ありません。Cプリプロセッサがどのように機能するかを真に観察しないことから、多くのエラーに気づきました。マクロパラメーターは常にラップします。式が関係する可能性はありません。


これは正しいです; 私の例は悪いものでした。しかし、それは実際に起こるべきことです。前に述べたようp + 1に、最終的にはポインター型になり、マクロ全体を無効にします(ポインターパラメーターを使用して関数でマクロを使用しようとした場合と同様です)。

結局のところ、この特定の例では、タイプが「配列」の式がないため、障害は本当に問題ではありません(つまり、私はみんなの時間を無駄にしているだけです。しかし、実際にはプリプロセッサの評価に関する微妙な点は重要だと思います。


2
説明ありがとう。元のバージョンではコンパイル時エラーが発生します。Clangは「添え字付きの値は配列、ポインター、またはベクトルではない」と報告します。マクロでの評価順序に関するコメントはよく理解されていますが、これはこのインスタンスでは望ましい動作のようです。
マークハリソン

1
私はコンパイラの不満を間違ったタイプの自動通知とは考えていませんでした。ありがとうございました!

3
使用しない理由はあります(sizeof (x) / sizeof (*x))か?
seriousdev

16

以下のために多次元配列には、少しはより複雑です。多くの場合、人々は明示的なマクロ定数、すなわち

#define g_rgDialogRows   2
#define g_rgDialogCols   7

static char const* g_rgDialog[g_rgDialogRows][g_rgDialogCols] =
{
    { " ",  " ",    " ",    " 494", " 210", " Generic Sample Dialog", " " },
    { " 1", " 330", " 174", " 88",  " ",    " OK",        " " },
};

ただし、これらの定数は、sizeofを使用してコンパイル時に評価することもできます。

#define rows_of_array(name)       \
    (sizeof(name   ) / sizeof(name[0][0]) / columns_of_array(name))
#define columns_of_array(name)    \
    (sizeof(name[0]) / sizeof(name[0][0]))

static char* g_rgDialog[][7] = { /* ... */ };

assert(   rows_of_array(g_rgDialog) == 2);
assert(columns_of_array(g_rgDialog) == 7);

このコードはCおよびC ++で機能することに注意してください。3次元以上の配列の場合

sizeof(name[0][0][0])
sizeof(name[0][0][0][0])

など、無限に。


15
sizeof(array) / sizeof(array[0])

タイプに依存してはarray、あなたが使用する必要はありませんしているsizeof(array) / sizeof(array[0])場合arrayの配列のいずれかであるcharunsigned charまたはsigned char- C18,6.5.3.4 / 4からの引用:「はsizeofはchar型、unsigned char型、またはsigned char型を持つオペランドに適用されると、またはその修飾バージョン)の結果は1です。」この場合、あなたはsizeof(array)私の専用の答えで説明されているように簡単に行うことができます
RobertSは

15

Cでの配列のサイズ:

int a[10];
size_t size_of_array = sizeof(a);      // Size of array a
int n = sizeof (a) / sizeof (a[0]);    // Number of elements in array a
size_t size_of_element = sizeof(a[0]); // Size of each element in array a                                          
                                       // Size of each element = size of type

4
おさるのコードが使用されていることsize_t size_of_elementはまだintint n = sizeof (a) / sizeof (a[0]); はなくsize_t n = sizeof (a) / sizeof (a[0]);
chux -復職モニカを

1
こんにちは@Yogeesh HTです。chuxの疑問に答えてください。また、int n = sizeof(a)/ sizeof(a [0])が配列の長さをどのように提供しているのか、なぜ配列の長さにsize_tを使用していないのかを知りたいと思っています。誰でも答えられますか?

1
@Brain sizeof(a)は、配列に存在するすべての要素のsizeofを提供します。sizeof(a [0])は、最初の要素のsizeofを提供します。a = {1,2,3,4,5};と仮定します。sizeof(a)= 20バイト(sizeof(int)= 4bytesに5を掛けた場合)、sizeof(a [0])= 4bytes、つまり20/4 = 5、つまり要素数がない
Yogeesh HT

2
@YogeeshHTのように非常に大きな配列の場合char a[INT_MAX + 1u];int nでの使用でint n = sizeof (a) / sizeof (a[0]);は不十分です(UBです)。を使用size_t n = sizeof (a) / sizeof (a[0]);してもこの問題は発生しません。
chux-モニカを

13

私がsizeofここで示す最後の2つのケースである、要素の数またはバイトのいずれかで、配列の2つの異なるサイズのいずれかを取得するために(たとえ使用できるとしても)決して使用しないことをお勧めします。2つのサイズのそれぞれについて、以下に示すマクロを使用して、より安全にすることができます。その理由は、コードの意図をメンテナに明らかにし、一見したところ(このように書かれたものは明らかではありません)との違いsizeof(ptr)sizeof(arr)明らかにし、コードを読むすべての人にとってバグが明らかになるようにするためです。


TL; DR:

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

#define ARRAY_BYTES(arr)    (sizeof((arr)[0]) * ARRAY_SIZE(arr))

must_be_array(arr)(以下で定義)-Wsizeof-pointer-divバギーと同様に必要です(2020年4月現在):

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

このトピックに関して重要なバグがありました:https : //lkml.org/lkml/2015/9/3/428

Linusが提供するソリューションには同意しません。これは、関数のパラメーターに配列表記を使用しないことです。

私は、ポインタが配列として使用されているというドキュメントとしての配列表記が好きです。しかし、それはバグのあるコードを書くことができないように、誰にでもできるソリューションを適用する必要があることを意味します。

配列から、3つのサイズがわかります。

  • 配列の要素のサイズ
  • 配列の要素数
  • 配列がメモリで使用するバイト単位のサイズ

配列の要素のサイズ

最初の方法は非常に単純で、配列とポインターのどちらを処理しても問題はありません。これは同じように行われるためです。

使用例:

void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
        qsort(arr, nmemb, sizeof(arr[0]), cmp);
}

qsort() 3番目の引数としてこの値が必要です。


質問のトピックである他の2つのサイズについては、配列を処理していることを確認し、そうでない場合はコンパイルを中断します。ポインターを処理している場合、間違った値が取得されるためです。 。コンパイルが失敗すると、配列ではなくポインターを使用していたことを簡単に確認できます。変数のサイズまたはマクロを格納するマクロを使用してコードを記述するだけで、ポインタの後ろの配列。


配列の要素数

これが最も一般的であり、多くの答えが典型的なマクロARRAY_SIZEを提供しています:

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]))

ARRAY_SIZEの結果はタイプの符号付き変数で一般的に使用さptrdiff_tれるため、このマクロの符号付きバリアントを定義することをお勧めします。

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

PTRDIFF_MAXメンバーを超える配列は、この署名されたバージョンのマクロに対して無効な値を提供しますが、C17 :: 6.5.6.9を読み取ることから、そのような配列はすでに火で遊んでいます。のみARRAY_SIZEsize_tこれらの例で使用されるべきです。

GCC 8などのコンパイラの最近のバージョンでは、このマクロをポインタに適用すると警告が表示されるため、安全です(古いコンパイラで安全にする方法は他にもあります)。

これは、配列全体のバイト単位のサイズを各要素のサイズで割ることによって機能します。

使用例:

void foo(ptrdiff_t nmemb)
{
        char buf[nmemb];

        fgets(buf, ARRAY_SIZE(buf), stdin);
}

void bar(ptrdiff_t nmemb)
{
        int arr[nmemb];

        for (ptrdiff_t i = 0; i < ARRAY_SSIZE(arr); i++)
                arr[i] = i;
}

これらの関数が配列を使用せず、代わりにそれらをパラメーターとして取得した場合、以前のコードはコンパイルされないため、バグが発生する可能性はありません(最新のコンパイラバージョンが使用されているか、他のトリックが使用されている場合)。 、そしてマクロ呼び出しを値で置き換える必要があります:

void foo(ptrdiff_t nmemb, char buf[nmemb])
{

        fgets(buf, nmemb, stdin);
}

void bar(ptrdiff_t nmemb, int arr[nmemb])
{

        for (ptrdiff_t i = 0; i < nmemb; i++)
                arr[i] = i;
}

配列がメモリで使用するバイト単位のサイズ

ARRAY_SIZE 前のケースの解決策として一般的に使用されますが、このケースがあまり一般的でないためか、このケースが安全に作成されることはほとんどありません。

この値を取得する一般的な方法は、 sizeof(arr)です。問題:前の問題と同じ。配列の代わりにポインタがある場合、プログラムはおかしくなります。

この問題の解決策には、以前と同じマクロを使用することが含まれていますが、これは安全であることがわかっています(ポインターに適用するとコンパイルが失敗します)。

#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

どのように機能するかは非常に簡単です。除算を元に戻すARRAY_SIZEので、数学的取り消しの後、最終的には1つだけになりますsizeof(arr)が、ARRAY_SIZE構造のれます。

使用例:

void foo(ptrdiff_t nmemb)
{
        int arr[nmemb];

        memset(arr, 0, ARRAY_BYTES(arr));
}

memset() 3番目の引数としてこの値が必要です。

以前と同様に、配列がパラメーター(ポインター)として受け取られた場合、それはコンパイルされず、マクロ呼び出しを値で置き換える必要があります。

void foo(ptrdiff_t nmemb, int arr[nmemb])
{

        memset(arr, 0, sizeof(arr[0]) * nmemb);
}

アップデート(23 / apr / 2020):-Wsizeof-pointer-divバグがある

今日、GCCの新しい警告は、マクロがシステムヘッダーではないヘッダーで定義されている場合にのみ機能することがわかりました。システムにインストールされているヘッダーでマクロを定義すると(通常は/usr/local/include/または/usr/include/)(#include <foo.h>)、コンパイラーは警告を発行しません(GCC 9.3.0を試しました)。

したがって、私たちは#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))それを安全にする必要があります。C11 _Static_assert()といくつかのGCC拡張が必要になります:式のステートメントと宣言__builtin_types_compatible_p

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        sizeof(arr) / sizeof((arr)[0]);                                \
}                                                                       \
)

ARRAY_SIZE()は完全に安全であり、したがってそのすべての派生物は安全です。


更新:libbsdは以下を提供します__arraycount()

Libbsdはでマクロ__arraycount()を提供しますが<sys/cdefs.h>、これには括弧のペアがないため安全ではありませんが、それらの括弧を自分で追加できるため、ヘッダーに除算を記述する必要さえありません(なぜ既存のコードを複製するのでしょうか? )。そのマクロはシステムヘッダーで定義されているため、使用する場合は上記のマクロを使用する必要があります。

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        __arraycount((arr));                                            \
}                                                                       \
)

#define ARRAY_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

一部のシステムnitems()<sys/param.h>代わりに提供し、一部のシステムは両方を提供します。あなたはあなたのシステムをチェックし、あなたが持っているものを使うべきです、そしておそらく移植性と両方をサポートするためにいくつかのプリプロセッサ条件を使います。


更新:マクロをファイルスコープで使用できるようにします。

残念ながら、({})gcc拡張子はファイルスコープでは使用できません。マクロをファイルスコープで使用できるようにするには、静的アサーションが内にある必要がありsizeof(struct {})ます。次に、0結果に影響を与えないように乗算します。へのキャストは(int)、戻る関数をシミュレートするのに適している場合があります(int)0(この場合は必要ありませんが、他のものに再利用できます)。

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

#define ARRAY_SIZE(arr)         (__arraycount((arr)) + must_be_array(arr))
#define ARRAY_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

3
なぜ反対票を投じるのか説明してもらえますか?これは、sizeof(arr)他では表示されていない、安全で一般的な構造()の解決策を示していますARRAY_BYTES(arr)
カカウエテフリト

2
ARRAY_SIZEは自由に使用できるほど一般的であり、ARRAY_BYTESはその名前で非常に明示的です。ARRAY_SIZEの隣に定義して、ユーザーが簡単に両方を確認できるようにする必要があります。します。私が意味したのは、単純なを使用せずsizeof、代わりにこの構造を使用することです。これらの構造を毎回書きたいと思うと、間違いを犯す可能性があります(貼り付けをコピーする場合は非常に一般的ですが、括弧が多いため毎回書く場合も非常に一般的です)...
Cacahuete Frito

3
...、だから私は主な結論に立ちます:シングルsizeofは明らかに安全ではありません(理由は答えにあります)、そしてマクロを使用せずに、毎回提供した構造を使用することは、さらに安全ではないので、行く唯一の方法マクロです。
カカウエテフリト

3
@MarkHarrisonポインタと配列の違いを知っています。しかし、後で小さな関数にリファクタリングした関数がありました。最初は配列でしたが、後でポインタになりました。これは、sizeofを変更するのを忘れた場合、それをねじ込むと、簡単に見えなくなります。それらの1つ。
カカウエテフリト

3
@hydeまた、違いがわかっているからといって、誰もが違いを知っているわけではないので、基本的にそれらのバグを100%取り除くものを使用しないのはなぜですか。そのバグはほとんどLinuxに侵入しました。それはLinusに届きました。つまり、それは多くの精査に合格し、カーネルの別の部分で同じバグがLinuxに侵入した可能性があることを意味しています。
カカウエテフリト

11

「あなたは足で自分を撃つ微妙な方法を導入しました」

Cの「ネイティブ」配列はサイズを格納しません。したがって、配列の長さを別の変数/定数に保存し、配列を渡すときはいつでも渡すことをお勧めします。

#define MY_ARRAY_LENGTH   15
int myArray[MY_ARRAY_LENGTH];

あなたは常にネイティブ配列を避けるべきです(あなたが足を気にすることができない場合を除いて)。C ++を記述している場合は、STLの「ベクター」コンテナを使用します。「配列と比較して、それらはほとんど同じパフォーマンスを提供します」、そしてそれらははるかに便利です!

// vector is a template, the <int> means it is a vector of ints
vector<int> numbers;  

// push_back() puts a new value at the end (or back) of the vector
for (int i = 0; i < 10; i++)
    numbers.push_back(i);

// Determine the size of the array
cout << numbers.size();

参照:http : //www.cplusplus.com/reference/stl/vector/


Cで整数定数を宣言する適切な方法は、enum宣言を使用することであると読みました。
Raffi Khatchadourian 2013

問題は、C ++ではなくCに関するものです。したがって、STLはありません。
カカウエテフリト


5

本当にこれを実行して配列を渡したい場合は、配列が必要な型へのポインタと配列のサイズを表す整数を格納する構造を実装することをお勧めします。次に、それを関数に渡すことができます。配列変数値(最初の要素へのポインター)をそのポインターに割り当てるだけです。次にArray.arr[i]、i番目の要素を取得して使用できますArray.sizeを取得し、して配列の要素数を取得できます。

私はあなたのためにいくつかのコードを含めました。あまり便利ではありませんが、より多くの機能で拡張できます。正直なところ、これらが必要な場合は、Cの使用を中止し、これらの機能が組み込まれた別の言語を使用する必要があります。

/* Absolutely no one should use this...
   By the time you're done implementing it you'll wish you just passed around
   an array and size to your functions */
/* This is a static implementation. You can get a dynamic implementation and 
   cut out the array in main by using the stdlib memory allocation methods,
   but it will work much slower since it will store your array on the heap */

#include <stdio.h>
#include <string.h>
/*
#include "MyTypeArray.h"
*/
/* MyTypeArray.h 
#ifndef MYTYPE_ARRAY
#define MYTYPE_ARRAY
*/
typedef struct MyType
{
   int age;
   char name[20];
} MyType;
typedef struct MyTypeArray
{
   int size;
   MyType *arr;
} MyTypeArray;

MyType new_MyType(int age, char *name);
MyTypeArray newMyTypeArray(int size, MyType *first);
/*
#endif
End MyTypeArray.h */

/* MyTypeArray.c */
MyType new_MyType(int age, char *name)
{
   MyType d;
   d.age = age;
   strcpy(d.name, name);
   return d;
}

MyTypeArray new_MyTypeArray(int size, MyType *first)
{
   MyTypeArray d;
   d.size = size;
   d.arr = first;
   return d;
}
/* End MyTypeArray.c */


void print_MyType_names(MyTypeArray d)
{
   int i;
   for (i = 0; i < d.size; i++)
   {
      printf("Name: %s, Age: %d\n", d.arr[i].name, d.arr[i].age);
   }
}

int main()
{
   /* First create an array on the stack to store our elements in.
      Note we could create an empty array with a size instead and
      set the elements later. */
   MyType arr[] = {new_MyType(10, "Sam"), new_MyType(3, "Baxter")};
   /* Now create a "MyTypeArray" which will use the array we just
      created internally. Really it will just store the value of the pointer
      "arr". Here we are manually setting the size. You can use the sizeof
      trick here instead if you're sure it will work with your compiler. */
   MyTypeArray array = new_MyTypeArray(2, arr);
   /* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
   print_MyType_names(array);
   return 0;
}

1
strcpy(d.name, name);オーバーフローの処理を行わないコードには賛成できません。
chux-モニカを2016年

4

最良の方法は、たとえば、この情報を構造に保存することです。

typedef struct {
     int *array;
     int elements;
} list_s;

作成、破棄、等価性のチェックなど、必要なすべての機能を実装します。パラメータとして渡す方が簡単です。


6
int elements対 何らかの理由size_t elements
chux-モニカを2016年

3

関数sizeofは、メモリ内の配列で使用されるバイト数を返します。配列の要素数を計算する場合は、その数をsizeof配列の変数の型で除算する必要があります。たとえばint array[10];、コンピューターの変数型整数が32ビット(または4バイト)の場合、配列のサイズを取得するには、次のようにする必要があります。

int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);

2

&演算子を使用できます。ここにソースコードがあります:

#include<stdio.h>
#include<stdlib.h>
int main(){

    int a[10];

    int *p; 

    printf("%p\n", (void *)a); 
    printf("%p\n", (void *)(&a+1));
    printf("---- diff----\n");
    printf("%zu\n", sizeof(a[0]));
    printf("The size of array a is %zu\n", ((char *)(&a+1)-(char *)a)/(sizeof(a[0])));


    return 0;
};

これがサンプル出力です

1549216672
1549216712
---- diff----
4
The size of array a is 10

7
私は反対投票しませんでしたが、これはハンマーが隣に横になっていることに気付かなかったので、レンガで釘を打つようなものです。また、人々は初期化されていない変数を使用することに眉をひそめる傾向があります...しかし、ここではそれがあなたの目的を十分に果たしていると思います。
ドミトリ

2
@Dmitri初期化されていない変数はここではアクセスされません
MM

1
うーん。ポインター減算はにつながりptrdiff_tます。 sizeof()結果はsize_tです。Cは、どちらがより広いか、より高いか、または同じランクであるかを定義しませ。したがって、商のタイプは((char *)(&a+1)-(char *)a)/(sizeof(a[0]))確実size_tではなく、したがってで印刷するzとUBになる可能性があります。単に使うだけprintf("The size of array a is %zu\n", sizeof a/sizeof a[0]);で十分です。
chux-モニカを2016年

1
(char *)(&a+1)-(char *)aは定数ではなく、固定サイズであっても実行時に計算される場合がありますa[10]sizeof(a)/sizeof(a[0])この場合、コンパイル時に常に行われます。
chux-モニカを

1

最も簡単な答え:

#include <stdio.h>

int main(void) {

    int a[] = {2,3,4,5,4,5,6,78,9,91,435,4,5,76,7,34};//for Example only
    int size;

    size = sizeof(a)/sizeof(a[0]);//Method

    printf ("size = %d",size);
    return 0;
}


0

すでに提供されている回答に加えて、私は次の使用によって特別なケースを指摘したいと思います

sizeof(a) / sizeof (a[0])

aがの配列の場合charunsigned charまたはこれらの型の1つのオペランドを持つ式の結果が常にになるため、2回signed char使用する必要はありません。sizeofsizeof1

C18,6.5.3.4 / 4からの引用:

ときsizeofタイプを有するオペランドに適用されるcharunsigned charまたはsigned char、(またはその修飾バージョン)の結果です1」。

したがって、if が、またはのタイプの配列である場合sizeof(a) / sizeof (a[0])と同等になります。1による除算は冗長です。NUMBER OF ARRAY ELEMENTS / 1acharunsigned charsigned char

この場合、単純に省略して実行できます。

sizeof(a)

例えば:

char a[10];
size_t length = sizeof(a);

証明が必要な場合は、ここにGodBoltへのリンクがあります。


それでも、タイプが大幅に変更された場合、部門は安全を維持します(これらのケースはまれです)。


1
タイプは将来変更される可能性がありますが(おそらくそうではないかもしれませんが)、除算でマクロを適用することを好むでしょう。除算はコンパイル時にわかっているため、コンパイラーは最適化を行います(変更しない場合)。コンパイラ)。
カカウエテフリト

1
@CacahueteFritoはい、その間も考えました。私はそれをサイドノートとして答えに取り入れました。ありがとうございました。
RobertSはモニカチェリオ

-1

注:これは、コメントでMMによって指摘されている未定義の動作を提供する可能性があります。

int a[10];
int size = (*(&a+1)-a) ;

詳細は こちらこちらご覧ください


2
これは技術的に未定義の動作です。*オペレータは、過去エンドポインタに適用されなくてもよい
MM

3
「未定義の動作」とは、C標準で動作が定義されていないことを意味します。プログラムで試してみると、何かが起こる可能性があります
MM

@MMあなたが言っている*(&a+1) - a;とは異なり(&a)[1] - a;、上記していないの両方 *(&a+1)(&a)[1]エンド過去1としてカウント?
QuentinUK

@QuentinUKあなたの2つの式はどちらも同じで、x[y]次のように定義されます*(x + (y))
MM

@MM私はそう思いました。しかし、他の答えは、Arjun Sreedharanによるもので、38の上向き矢印があり、これには-1があります。そして、Arjun Sreedharanの回答には、未定義の動作についての言及はありません。
QuentinUK
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.