web-dev-qa-db-ja.com

'sizeof'(配列を指すポインタ)を見つける方法は?

最初に、ここにいくつかのコードがあります:

int main() 
{
    int days[] = {1,2,3,4,5};
    int *ptr = days;
    printf("%u\n", sizeof(days));
    printf("%u\n", sizeof(ptr));

    return 0;
}

ptrが指している配列のサイズを調べる方法はありますか(32ビットシステムでは4バイトのサイズを指定するのではなく)。

279
jkidv

いいえ、できません。コンパイラは、ポインタが指しているものを知りません。既知の帯域外の値で配列を終了し、その値までサイズをカウントアップするなどのトリックがありますが、それはsizeofを使用していません。

もう1つのトリックは、 Zan で言及されているものです。これは、サイズをどこかに隠すことです。たとえば、動的に配列を割り当てる場合は、必要なブロックより1大きいintのブロックを割り当て、最初のintにサイズを隠し、配列へのポインタとしてptr + 1を返します。サイズが必要なときは、ポインタをデクリメントして隠し値を覗きます。配列だけでなく、ブロック全体を最初から解放することを忘れないでください。

242
Paul Tomblin

答えはノーだ。"

Cプログラマがすることは、配列のサイズをどこかに格納することです。それは構造の一部であるかもしれません、またはプログラマーは配列の始まりの前に長さの値を格納するために要求されるより少し多くのメモリとmalloc()より多くのメモリをカンニングすることができます。

76
Zan Lynx

動的配列(mallocまたはC++new)では、他の人が述べたように配列のサイズを格納するか、おそらく追加、削除、カウントなどを処理する配列マネージャ構造体。残念ながら、CはC++と同じようにこれを実行しません。保存する配列の種類ごとに構築する必要があるためです。あなたが管理する必要があること。

あなたの例のような静的な配列のために、サイズを得るために使われる共通のマクロがあります、しかしそれがパラメータが本当にaであるかどうかチェックしないので推奨されません静的配列ただし、マクロは実際のコードで使用されます。 Linuxカーネルのヘッ​​ダにはありますが、以下のものとは多少異なる場合があります。

#if !defined(ARRAY_SIZE)
    #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif

int main()
{
    int days[] = {1,2,3,4,5};
    int *ptr = days;
    printf("%u\n", ARRAY_SIZE(days));
    printf("%u\n", sizeof(ptr));
    return 0;
}

このようなマクロには用心することが理由でグーグルできます。注意してください。

可能であれば、vectorなどのC++ stdlibを使用すると、はるかに安全で使いやすくなります。

44
Ryan

sizeof()を使用せずに、C++テンプレートを使用したクリーンな解決方法があります。次のgetSize()関数は、静的配列のサイズを返します。

#include <cstddef>

template<typename T, size_t SIZE>
size_t getSize(T (&)[SIZE]) {
    return SIZE;
}

これは、foo_t構造の例です。

#include <cstddef>

template<typename T, size_t SIZE>
size_t getSize(T (&)[SIZE]) {
    return SIZE;
}

struct foo_t {
    int ball;
};

int main()
{
    foo_t foos3[] = {{1},{2},{3}};
    foo_t foos5[] = {{1},{2},{3},{4},{5}};
    printf("%u\n", getSize(foos3));
    printf("%u\n", getSize(foos5));

    return 0;
}

出力:

3
5
17
skurton

この特定の例では、はい、typedefを使用している場合はそうです(下記参照)。もちろん、このようにすれば、SIZEOF_DAYSを使用することもできます。ポインタが何を指しているのかがわかっているからです。

Malloc()などで返されるように(void *)ポインタがある場合、no、ポインタがどのデータ構造を指しているかを判断する方法、つまりそのサイズを判断する方法はありません。

#include <stdio.h>

#define NUM_DAYS 5
typedef int days_t[ NUM_DAYS ];
#define SIZEOF_DAYS ( sizeof( days_t ) )

int main() {
    days_t  days;
    days_t *ptr = &days; 

    printf( "SIZEOF_DAYS:  %u\n", SIZEOF_DAYS  );
    printf( "sizeof(days): %u\n", sizeof(days) );
    printf( "sizeof(*ptr): %u\n", sizeof(*ptr) );
    printf( "sizeof(ptr):  %u\n", sizeof(ptr)  );

    return 0;
} 

出力:

SIZEOF_DAYS:  20
sizeof(days): 20
sizeof(*ptr): 20
sizeof(ptr):  4
5
David

魔法のような解決策はありません。 Cは反省的な言語ではありません。オブジェクトはそれらが何であるかを自動的には知りません。

しかし、あなたは多くの選択肢があります。

  1. 明らかに、パラメータを追加する
  2. 呼び出しをマクロで囲み、自動的にパラメータを追加する
  3. より複雑なオブジェクトを使用してください。動的配列と配列のサイズを含む構造体を定義します。次に、構造体のアドレスを渡します。
4
DigitalRoss

すべての正解が述べているように、配列の減衰したポインタ値だけからこの情報を取得することはできません。減衰したポインタが関数によって受信された引数である場合、関数がそのサイズを認識するようにするには、他の方法で元の配列のサイズを指定する必要があります。

これまでに提供されたものとは異なる提案がここにあります、それはうまくいくでしょう:代わりに配列へのポインタを渡してください。この提案はC++スタイルの提案と似ていますが、Cがテンプレートや参照をサポートしていない点が異なります。

#define ARRAY_SZ 10

void foo (int (*arr)[ARRAY_SZ]) {
    printf("%u\n", (unsigned)sizeof(*arr)/sizeof(**arr));
}

ただし、関数は渡される配列のサイズを正確に認識するように定義されているため、この提案はあなたの問題にとっては馬鹿げたものです(したがって、配列にsizeofを使用する必要はほとんどありません)。しかしそれがすることは、ある種の安全性を提供することです。それはあなたが望まないサイズの配列を渡すことを禁止します。

int x[20];
int y[10];
foo(&x); /* error */
foo(&y); /* ok */

関数が任意のサイズの配列で動作できると想定される場合は、追加情報として関数にサイズを指定する必要があります。

3
jxh
int main() 
{
    int days[] = {1,2,3,4,5};
    int *ptr = days;
    printf("%u\n", sizeof(days));
    printf("%u\n", sizeof(ptr));

    return 0;
}

日数のサイズ[]は20で、要素の数ではありません*データ型のサイズです。ポインターのサイズは4ですが、ポインターのサイズは関係ありません。ポインタはそのアドレスを格納することによって他の要素を指すためです。

2

この問題に対する私の解決策は、配列に関するメタ情報として、配列の長さをstruct Arrayに保存することです。

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

struct Array
{
    int length;

    double *array;
};

typedef struct Array Array;

Array* NewArray(int length)
{
    /* Allocate the memory for the struct Array */
    Array *newArray = (Array*) malloc(sizeof(Array));

    /* Insert only non-negative length's*/
    newArray->length = (length > 0) ? length : 0;

    newArray->array = (double*) malloc(length*sizeof(double));

    return newArray;
}

void SetArray(Array *structure,int length,double* array)
{
    structure->length = length;
    structure->array = array;
}

void PrintArray(Array *structure)
{       
    if(structure->length > 0)
    {
        int i;
        printf("length: %d\n", structure->length);
        for (i = 0; i < structure->length; i++)
            printf("%g\n", structure->array[i]);
    }
    else
        printf("Empty Array. Length 0\n");
}

int main()
{
    int i;
    Array *negativeTest, *days = NewArray(5);

    double moreDays[] = {1,2,3,4,5,6,7,8,9,10};

    for (i = 0; i < days->length; i++)
        days->array[i] = i+1;

    PrintArray(days);

    SetArray(days,10,moreDays);

    PrintArray(days);

    negativeTest = NewArray(-5);

    PrintArray(negativeTest);

    return 0;
}

しかし、あなたが保存したい配列の正しい長さを設定することに気をつけなければなりません。なぜなら、私たちの友人が大々的に説明したように、この長さをチェックする方法はないからです。

2
user4713908

あなたはこのようなことをすることができます:

int days[] = { /*length:*/5, /*values:*/ 1,2,3,4,5 };
int *ptr = days + 1;
printf("array length: %u\n", ptr[-1]);
return 0;

いいえ、ptrが指している配列のサイズを見つけるためにsizeof(ptr)を使用することはできません。

あなたが余分なスペースに長さを保存したいならば、余分なメモリ(配列のサイズより多く)を割り当てることは役に立ちますが。

1
SKD
 #define array_size 10

 struct {
     int16 size;
     int16 array[array_size];
     int16 property1[(array_size/16)+1]
     int16 property2[(array_size/16)+1]
 } array1 = {array_size, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

 #undef array_size

array_sizeはサイズ変数に渡します。

#define array_size 30

struct {
    int16 size;
    int16 array[array_size];
    int16 property1[(array_size/16)+1]
    int16 property2[(array_size/16)+1]
} array2 = {array_size};

#undef array_size

使い方は次のとおりです。

void main() {

    int16 size = array1.size;
    for (int i=0; i!=size; i++) {

        array1.array[i] *= 2;
    }
}
0
user3065147