まず、ここにいくつかのコードがあります:
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バイトであるサイズを指定する代わりに)指している配列のサイズを確認する方法はありますか?
まず、ここにいくつかのコードがあります:
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バイトであるサイズを指定する代わりに)指している配列のサイズを確認する方法はありますか?
回答:
いいえ、できません。コンパイラーは、ポインターが何を指しているのか知りません。既知の帯域外の値で配列を終了し、その値までサイズをカウントするようなトリックがありますが、それはを使用していませんsizeof()
。
もう1つのトリックは、Zanが言及したもので、サイズをどこかに隠しておきます。たとえば、配列を動的に割り当てる場合は、必要なブロックより1 int大きいブロックを割り当て、最初のintにサイズを隠しptr+1
、配列へのポインタとして返します。サイズが必要な場合は、ポインターをデクリメントして、隠された値をのぞきます。配列だけでなく、ブロック全体を最初から解放することを忘れないでください。
答えはいいえだ。"
Cプログラマが行うことは、配列のサイズをどこかに格納することです。構造体の一部にすることも、プログラマがmalloc()
配列の開始前に長さの値を格納するために、要求されたよりも少し多くのメモリをだますこともできます。
動的配列(mallocまたはC ++ new)の場合、他の人が述べたように配列のサイズを格納するか、追加、削除、カウントなどを処理する配列マネージャー構造を構築する必要があります。残念ながら、Cはこれをほぼ同様に実行しませんC ++は、基本的に、格納する配列の種類ごとにビルドする必要があるため、管理する必要のある配列の種類が複数ある場合は面倒です。
例のような静的配列の場合、サイズを取得するために使用される一般的なマクロがありますが、パラメーターが実際に静的配列であるかどうかをチェックしないため、お勧めできません。ただし、マクロは実際のコード、たとえば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。
ARRAY_SIZE
引数が配列(つまり、配列型の式)の場合、そのマクロは常に機能します。いわゆる「動的配列」では、実際の「配列」(配列型の式)を取得することはありません。(もちろん、配列タイプにはコンパイル時のサイズが含まれるため、できません。)最初の要素へのポインターを取得するだけです。「パラメーターが実際に静的配列であるかどうかをチェックしない」という異論は、1つは配列であり、もう1つはそうでないため異なるため、実際には有効ではありません。
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
T (&)[SIZE]
。これが何を意味するか説明できますか?また、このコンテキストでconstexprについて言及することもできます。
SIZE
、引数として渡されません。これは、関数の定義ですでに知られている。)
この特定の例では、はい、あります。typedefを使用する場合(下記を参照)。もちろん、このようにすると、ポインタが何を指しているかわかっているので、SIZEOF_DAYSを使用することもできます。
malloc()などによって返される(void *)ポインターがある場合、いいえ、ポインターが指しているデータ構造を判別する方法はないため、そのサイズを判別する方法はありません。
#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
すべての正しい答えが述べたように、配列の減衰したポインタ値だけからこの情報を取得することはできません。減衰したポインタが関数が受け取った引数である場合、元の配列のサイズは、関数がそのサイズを知るために他の方法で提供される必要があります。
これは、これまでに提供されたものとは異なる提案であり、機能します。代わりに、配列へのポインタを渡します。この提案は、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 */
関数が任意のサイズの配列を操作できるようになっている場合は、追加情報として関数にサイズを指定する必要があります。
魔法の解決策はありません。Cは反映言語ではありません。オブジェクトは、それらが何であるかを自動的に認識しません。
しかし、あなたには多くの選択肢があります:
この問題に対する私の解決策は、配列の長さを配列に関するメタ情報として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;
}
しかし、あなたが保存したい配列の正しい長さを設定することに注意する必要があります。なぜなら、私たちの友人が大々的に説明したように、この長さをチェックする方法はないからです。
あなたはこのようなことをすることができます:
int days[] = { /*length:*/5, /*values:*/ 1,2,3,4,5 };
int *ptr = days + 1;
printf("array length: %u\n", ptr[-1]);
return 0;
int main()
{
int days[] = {1,2,3,4,5};
int *ptr = days;
printf("%u\n", sizeof(days));
printf("%u\n", sizeof(ptr));
return 0;
}
days []のサイズは20で、これは要素数*データ型のサイズではありません。ポインタのサイズは4ですが、それが何を指しているかに関係ありません。ポインタは、アドレスを格納することによって他の要素を指すからです。
#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'
、末尾に文字があるため、などの関数を使用して文字列の長さを取得できますstrlen
。たとえば、整数配列の問題は、値を終了値として使用できないため、解決策の1つは、配列をアドレス指定し、終了値としてNULL
ポインターを使用することです。
#include <stdio.h>
/* the following function will produce the warning:
* ‘sizeof’ on array function parameter ‘a’ will
* return size of ‘int *’ [-Wsizeof-array-argument]
*/
void foo( int a[] )
{
printf( "%lu\n", sizeof a );
}
/* so we have to implement something else one possible
* idea is to use the NULL pointer as a control value
* the same way '\0' is used in strings but this way
* the pointer passed to a function should address pointers
* so the actual implementation of an array type will
* be a pointer to pointer
*/
typedef char * type_t; /* line 18 */
typedef type_t ** array_t;
int main( void )
{
array_t initialize( int, ... );
/* initialize an array with four values "foo", "bar", "baz", "foobar"
* if one wants to use integers rather than strings than in the typedef
* declaration at line 18 the char * type should be changed with int
* and in the format used for printing the array values
* at line 45 and 51 "%s" should be changed with "%i"
*/
array_t array = initialize( 4, "foo", "bar", "baz", "foobar" );
int size( array_t );
/* print array size */
printf( "size %i:\n", size( array ));
void aprint( char *, array_t );
/* print array values */
aprint( "%s\n", array ); /* line 45 */
type_t getval( array_t, int );
/* print an indexed value */
int i = 2;
type_t val = getval( array, i );
printf( "%i: %s\n", i, val ); /* line 51 */
void delete( array_t );
/* free some space */
delete( array );
return 0;
}
/* the output of the program should be:
* size 4:
* foo
* bar
* baz
* foobar
* 2: baz
*/
#include <stdarg.h>
#include <stdlib.h>
array_t initialize( int n, ... )
{
/* here we store the array values */
type_t *v = (type_t *) malloc( sizeof( type_t ) * n );
va_list ap;
va_start( ap, n );
int j;
for ( j = 0; j < n; j++ )
v[j] = va_arg( ap, type_t );
va_end( ap );
/* the actual array will hold the addresses of those
* values plus a NULL pointer
*/
array_t a = (array_t) malloc( sizeof( type_t *) * ( n + 1 ));
a[n] = NULL;
for ( j = 0; j < n; j++ )
a[j] = v + j;
return a;
}
int size( array_t a )
{
int n = 0;
while ( *a++ != NULL )
n++;
return n;
}
void aprint( char *fmt, array_t a )
{
while ( *a != NULL )
printf( fmt, **a++ );
}
type_t getval( array_t a, int i )
{
return *a[i];
}
void delete( array_t a )
{
free( *a );
free( a );
}
NULL
は、個別の要素をsize
直接格納するだけでは考えられない最も効率の悪い代替手段です。特に、この追加の間接層を常に実際に使用している場合はなおさらです。