'Sizeof' (diziyi gösteren bir işaretçi) nasıl bulunur?


309

İlk önce, bazı kodlar:

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İşaret eden dizinin boyutunu bulmanın bir yolu var mı (sadece 32 bit sistemde dört bayt olan boyutunu vermek yerine)?


84
Ben her zaman sizeof ile parens kullandım - bir işlev çağrısı gibi görünmesini sağlar, ancak daha net olduğunu düşünüyorum.
Paul Tomblin

20
Neden olmasın? Gereksiz parantezlere karşı bir şeyleriniz var mı? Bence onlarla biraz daha kolay okuyor, kendim.
David Thornley

6
@Paul: iyi .. bu çağrının sol tarafının int için bir işaretçi olduğunu varsayarsak, int * ptr = malloc (4 * sizeof * ptr); ki bu bana çok daha açık. Okumak için daha az parens ve matematikte olduğu gibi değişmez daimi ön tarafa getirmek.
açma

4
@ unwind - bir dizi int demek istediğinizde bir dizi işaretçi ayırmayın!
Paul Tomblin

6
Burada "bir diziyi gösteren işaretçi" yoktur. Sadece int işaret eden bir işaretçi.
newacct

Yanıtlar:


269

Hayır, yapamazsınız. Derleyici işaretçinin neye işaret ettiğini bilmiyor. Diziyi bilinen bir bant dışı değeriyle bitirmek ve ardından boyutu bu değere kadar saymak gibi hileler vardır, ancak bu kullanılmaz sizeof().

Başka bir hile, Zan'ın bahsettiği, bir yerlerde boyutu saklamaktır. Örneğin, diziyi dinamik olarak ayırıyorsanız, ihtiyacınız olandan bir int daha büyük bir blok atayın, ilk int'deki boyutu saklayın ptr+1ve diziye işaretçi olarak dönün . Boyuta ihtiyacınız olduğunda, işaretçiyi azaltın ve saklanan değere bakın. Sadece diziyi değil, baştan başlayarak tüm bloğu boşaltmayı unutmayın.


12
Bu kadar geç bir yorum gönderme için üzgünüm ama derleyici işaretçi ne işaret bilmiyorsa nasıl boş bellek ne kadar biliyor? Bu bilgilerin, ücretsiz kullanım gibi işlevler için dahili olarak saklandığını biliyorum. Benim sorum şu: 'neden derleyici bunu yapabilir?
viki.omega9

11
@ viki.omega9, çünkü ücretsiz çalışma zamanında boyutu bulur. Derleyici, boyutu bilmiyor çünkü çalışma zamanı faktörlerine (komut satırı bağımsız değişkenleri, bir dosyanın içeriği, ayın evresi, vb.) Bağlı olarak diziyi farklı bir boyut haline getirebilirsiniz.
Paul Tomblin

15
Hızlı takip, neden boyutu serbest şekilde geri döndüren bir fonksiyon yok?
viki.omega9

5
Eğer işlevin sadece hatalı hafıza ile çağrıldığını ve kütüphanenin hatalı hafızayı en çok gördüğüm şekilde izlediğini (döndürülen işaretçiden önce bir int kullanarak) izleyebilirseniz, bir tane yazabilirsiniz. Ancak işaretçi statik bir diziye veya benzerine giderse başarısız olur. Benzer şekilde, hatalı bellek boyutunun programınıza erişebileceğine dair bir garanti yoktur.
Paul Tomblin

9
@ viki.omega9: Akılda tutulması gereken başka bir şey, malloc / free sistemi tarafından kaydedilen boyutun istediğiniz boyutta olmaması olabilir. 9 bayt malloc ve 16 olsun. Malloc 3K bayt ve 4K olsun. Veya benzer durumlar.
Zan Lynx

85

Cevap hayır."

C programcılarının yaptığı şey dizinin boyutunu bir yerde saklamaktır. Bir yapının parçası olabilir veya programcı malloc(), dizinin başlangıcından önce bir uzunluk değerini saklamak için istenenden biraz daha fazla bellek kopyalayabilir.


3
Paskal dizeleri bu şekilde uygulanmaktadır
dsm

6
ve görünüşe göre pascal dizeleri excel neden bu kadar hızlı çalışır!
Adam Naylor

8
@Adam: Hızlı. Bunu benim dizelerin uygulanması listesinde kullanıyorum. Doğrusal aramaya göre süper hızlıdır: yük boyutu, önceden getirme pos + boyutu, boyutu arama boyutuyla karşılaştırın, eşit strncmp ise, bir sonraki dizeye geçin, tekrarlayın. Yaklaşık 500 dizeye kadar ikili aramadan daha hızlıdır.
Zan Lynx

47

Dinamik diziler için ( malloc veya C ++ yeni ) dizinin boyutunu başkaları tarafından belirtildiği gibi depolamanız veya belki de ekleme, kaldırma, sayma, vb. İşlemlerini gerçekleştiren bir dizi yöneticisi yapısı oluşturmanız gerekir. Temel olarak yönetmeniz gereken çok sayıda diziniz varsa sakıncalı olan her farklı dizi türü için oluşturmak zorunda olduğunuz için C ++.

Örneğinizde olduğu gibi statik diziler için, boyutu elde etmek için kullanılan ortak bir makro vardır, ancak parametrenin gerçekten statik bir dizi olup olmadığını kontrol etmediğinden önerilmez . Makro, gerçek kodda, örneğin Linux çekirdek başlıklarında kullanılır, ancak aşağıdaki koddan biraz farklı olabilir:

#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;
}

Bunun gibi makrolara karşı dikkatli olmanız için Google'ı kullanabilirsiniz. Dikkatli ol.

Mümkünse, vektör gibi C ++ stdlib çok daha güvenli ve kullanımı daha kolaydır.


11
ARRAY_SIZE, her yerde pratik programcılar tarafından kullanılan yaygın bir paradigmadır.
Sanjaya R

5
Evet, bu yaygın bir paradigma. Dinamik bir dizide unutmak ve kullanmak kolay olduğu için yine de dikkatli bir şekilde kullanmanız gerekir.
Ryan

2
Evet, iyi bir nokta, ama sorulan soru, işaretçi biriyle değil statik olanla ilgilidir.
Paul Tomblin

2
Bu ARRAY_SIZEmakro, argümanı bir dizi ise (yani dizi türünün ifadesi) her zaman çalışır. Sözde "dinamik dizi" için, asla gerçek bir "dizi" (dizi türünün ifadesi) elde edemezsiniz. (Tabii ki, dizi türleri derleme zamanında boyutlarını içerdiğinden yapamazsınız.) Sadece ilk öğeye bir işaretçi alırsınız. İtirazınız, "parametrenin gerçekten statik bir dizi olup olmadığını kontrol etmez" gerçekten geçerli değildir, çünkü bunlar bir dizi olduğu ve diğeri olmadığı için farklıdır.
newacct

2
Etrafta aynı şeyi yapan ancak işaretçilerin kullanımını engelleyecek bir şablon işlevi vardır.
Natalie Adams

18

C ++ şablonları ile sizeof () kullanmadan temiz bir çözüm var . Aşağıdaki getSize () işlevi, herhangi bir statik dizinin boyutunu döndürür:

#include <cstddef>

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

İşte foo_t yapısına sahip bir örnek :

#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;
}

Çıktı:

3
5

Notasyonu hiç görmedim T (&)[SIZE]. Bunun ne anlama geldiğini açıklayabilir misiniz? Ayrıca bu bağlamda constexpr'den bahsedebilirsiniz.
WorldSEnder

2
C ++ kullanıyorsanız ve aslında bir dizi türünde bir değişkeniniz varsa, bu güzel. Bunların hiçbiri soruda geçerli değildir: Dil C'dir ve OP'nin dizi boyutunu almak istediği şey basit bir işaretçi.
Oguk

Bu kod, her farklı boyut / tip kombinasyonu için aynı kodu yeniden oluşturarak kod blokajına yol açar mı yoksa bu derleyici tarafından sihirli bir şekilde varlığını optimize eder mi?
user2796283

@WorldSEnder: Dizi türü başvurusu için C ++ sözdizimi (değişken adı olmadan, yalnızca boyut ve öğe türü).
Peter Cordes

@ user2796283: Bu işlev tamamen derleme zamanında optimize edilmiştir; sihire gerek yok; hiçbir şeyi tek bir tanımla birleştirmez, sadece derleme zamanı sabitine yerleştirir. (Ancak bir hata ayıklama derlemesinde, evet, farklı sabitler döndüren bir grup ayrı fonksiyonunuz olur. Bağlayıcı büyüsü aynı sabiti kullananları birleştirebilir. Arayan SIZEarg olarak geçmez , bu bir şablon parametresidir. işlev tanımıyla zaten bilinir.)
Peter Cordes

5

Bu özel örnek için, evet, typedefs kullanırsanız (aşağıya bakın). Tabii ki, bu şekilde yaparsanız, işaretçinin neye işaret ettiğini bildiğiniz için SIZEOF_DAYS'i kullanmak kadar iyi değilsiniz.

Eğer malloc () ya da benzerleri tarafından döndürülen bir (void *) işaretçiniz varsa, o zaman hayır, işaretçinin hangi veri yapısını gösterdiğini ve dolayısıyla boyutunu belirlemenin bir yolu yoktur.

#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;
} 

Çıktı:

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

5

Tüm doğru cevapların belirttiği gibi, bu bilgiyi sadece dizinin çürümüş işaretçi değerinden alamazsınız. Çürümüş işaretçi işlev tarafından alınan argüman ise, işlevin bu boyutu tanıması için kaynak dizinin boyutu başka bir şekilde sağlanmalıdır.

İşte şimdiye kadar sağlananlardan farklı bir öneri, işe yarayacak: Bunun yerine diziye bir işaretçi geçirin. Bu öneri, C'nin şablonları veya başvuruları desteklememesi dışında C ++ stil önerilerine benzer:

#define ARRAY_SZ 10

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

Ancak, bu öneri probleminiz için bir tür saçmadır, çünkü işlev geçirilen dizinin boyutunu tam olarak bilmek için tanımlanmıştır (bu nedenle, dizide sizeof'i kullanmaya çok az ihtiyaç vardır). Bununla birlikte yaptığı şey, bir tür güvenlik sunmaktır. İstenmeyen boyutta bir dizi geçirmenizi yasaklar.

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

İşlevin herhangi bir dizi boyutunda çalışabilmesi gerekiyorsa, işleve boyutu ek bilgi olarak sağlamanız gerekir.


1
+1 "Bu bilgiyi yalnızca dizinin çürümüş işaretçi değerinden alamazsınız ve geçici bir çözüm sağlarsınız.
Max

4

Sihirli bir çözüm yok. C yansıtıcı bir dil değildir. Nesneler ne olduklarını otomatik olarak bilmezler.

Ancak birçok seçeneğiniz var:

  1. Açıkçası, bir parametre ekleyin
  2. Aramayı bir makroya sarın ve otomatik olarak bir parametre ekleyin
  3. Daha karmaşık bir nesne kullanın. Dinamik diziyi ve dizinin boyutunu içeren bir yapı tanımlayın. Ardından, yapının adresini iletin.

Nesneler ne olduklarını bilirler. Ancak bir alt nesneye işaret ederseniz, tüm nesne veya daha büyük bir alt nesne hakkında bilgi almanın bir yolu yoktur
MM

2

Bu soruna benim çözüm dizinin uzunluğunu dizi hakkında bir meta-bilgi olarak bir yapı dizisine kaydetmek.

#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;
}

Ancak saklamak istediğiniz dizinin doğru uzunluğunu ayarlamanız gerekir, çünkü bu uzunluğu kontrol etmenin bir yolu yoktur, tıpkı arkadaşlarımızın kitlesel olarak açıkladığı gibi.


2

Bunun gibi bir şey yapabilirsiniz:

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

1

Hayır, sizeof(ptr)dizinin ptrişaret ettiği boyutu bulmak için kullanamazsınız .

Ekstra bellek ayırmak (dizinin boyutundan daha fazla) uzunluğunu fazladan alanda saklamak istiyorsanız yardımcı olacaktır.


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

    return 0;
}

Günlerin boyutu [], öğe türünden * olmayan 20'dir ve veri türünün boyutu. İşaretçi ne olursa olsun işaretçi boyutu 4'tür. Çünkü bir işaretçi diğer öğeyi adresini saklayarak işaret eder.


1
sizeof (ptr) ibre büyüklüğü ve sizeof (* ptr) ibre büyüklüğüdür
Amitābha

0
 #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 boyut değişkenine geçiyor :

#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

Kullanımı:

void main() {

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

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

0

Dizelerde '\0', sonunda işlevler kullanılarak dizenin uzunluğu elde edilebilen bir karakter vardır strlen. Örneğin, bir tamsayı dizisiyle ilgili sorun, bitiş değeri olarak herhangi bir değeri kullanamayacağınızdır, bu nedenle olası bir çözüm, diziyi adreslemek ve NULLişaretçiyi bir bitiş değeri olarak kullanmaktır .

#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 );
}

Kodunuz yorumlarla dolu, ancak bunun normal metin olarak kod dışında nasıl çalıştığına dair genel bir açıklama eklerseniz her şeyin daha kolay olacağını düşünüyorum. Lütfen sorunuzu düzenleyip yapabilir misiniz ? Teşekkür ederim!
Fabio, Reinstate Monica'ya

Her bir öğeye bir dizi işaretçi oluşturmak, onu doğrusal olarak arayabilmeniz için NULL, muhtemelen sizedoğrudan bir ayrı depolamak için hayal edilebilecek en az etkili alternatiftir . Özellikle de aslında eğer kullanmak dolaylama bu fazladan bir katman her zaman.
Peter Cordes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.