Jak znaleźć ' sizeof '(wskaźnik wskazujący na tablicę)?

Po pierwsze, oto jakiś kod:

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

    return 0;
}

Czy istnieje sposób, aby dowiedzieć się, jaki rozmiar tablicy ptr wskazuje na (zamiast podawać jej rozmiar, który wynosi cztery bajty w systemie 32-bitowym)?

Author: Jonathan Leffler, 2009-01-29

13 answers

Nie, Nie możesz. kompilator nie wie, na co wskazuje wskaźnik. Istnieją sztuczki, takie jak kończenie tablicy znaną wartością poza pasmem, a następnie liczenie rozmiaru do tej wartości, ale to nie jest użycie sizeof().

Kolejną sztuczką jest ta, o której wspomina Zan , która polega na ukryciu rozmiaru gdzieś. Na przykład, jeśli dynamicznie przydzielasz tablicę, przydziel blok o jedną int większą niż ta, której potrzebujesz, Schowaj rozmiar w pierwszej int i zwróć ptr+1 jako wskaźnik do tablicy. Gdy potrzebujesz rozmiaru, zmniejsz wskaźnik i zerknij na ukrytą wartość. Pamiętaj tylko, aby zwolnić cały blok od początku, a nie tylko tablicę.

 286
Author: Paul Tomblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-19 08:53:26

Odpowiedź brzmi: "nie."

Programiści C przechowują gdzieś rozmiar tablicy. Może być częścią struktury, lub programista może oszukać trochę i malloc() więcej pamięci niż wymaga, aby zapisać wartość długości przed rozpoczęciem tablicy.

 90
Author: Zan Lynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-19 08:53:47

Dla dynamicznych tablic (malloc lub C++ new) Musisz zapisać rozmiar tablicy, jak wspomniano przez innych lub być może zbudować strukturę menedżera tablic, która obsługuje dodawanie, usuwanie, liczenie itp. Niestety C nie robi tego prawie tak dobrze jak C++, ponieważ w zasadzie musisz zbudować go dla każdego innego typu tablicy, którą przechowujesz, co jest kłopotliwe, jeśli masz wiele typów tablic, którymi musisz zarządzać.

Dla tablic statycznych, takich jak ta w twoim przykładzie, istnieje powszechne makro używane do uzyskania rozmiaru, ale nie jest to zalecane , ponieważ nie sprawdza, czy parametr jest rzeczywiście tablicą statyczną. Makro jest jednak używane w rzeczywistym kodzie, np. w nagłówkach jądra Linuksa, chociaż może być nieco inne niż poniższe:

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

Możesz wygooglować z powodów, aby być ostrożnym z makrami takimi jak ten. Bądź ostrożny.

Jeśli to możliwe, stdlib C++, takie jak vector, który jest znacznie bezpieczniejszy i łatwiejszy w użyciu.

 49
Author: Ryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-14 22:53:31

Istnieje czyste rozwiązanie z szablonami C++, bez użycia sizeof(). Następująca funkcja getSize () zwraca rozmiar dowolnej tablicy statycznej:

#include <cstddef>

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

Oto przykład ze strukturą 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;
}

Wyjście:

3
5
 18
Author: skurton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-20 12:18:11

Jak stwierdziły wszystkie poprawne odpowiedzi, nie można uzyskać tej informacji z zepsutej wartości wskaźnika samej tablicy. Jeśli wskaźnik rozpadu jest argumentem otrzymanym przez funkcję, to rozmiar tablicy źródłowej musi być podany w inny sposób, aby funkcja poznała ten rozmiar.

Oto sugestia inna niż do tej pory, która zadziała: podaj wskaźnik do tablicy. Ta sugestia jest podobna do stylu C++ sugestie, tyle że C nie obsługuje szablonów ani odnośników:

#define ARRAY_SZ 10

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

Ale ta sugestia jest trochę głupia dla Twojego problemu, ponieważ funkcja jest zdefiniowana tak, aby dokładnie znać rozmiar tablicy, która jest przekazywana (stąd nie ma potrzeby używania sizeof w ogóle na tablicy). To, co robi, to oferuje pewne bezpieczeństwo typu. To zabroni ci przechodzenia w tablicy niechcianego rozmiaru.

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

Jeśli funkcja ma być w stanie działać na dowolnym rozmiarze array, wtedy będziesz musiał podać rozmiar funkcji jako dodatkowe informacje.

 6
Author: jxh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-09 17:21:56

Dla tego konkretnego przykładu, tak, jest, jeśli używasz typedefs (patrz poniżej). Oczywiście, jeśli zrobisz to w ten sposób, równie dobrze możesz użyć SIZEOF_DAYS, ponieważ wiesz, na co wskazuje wskaźnik.

Jeśli masz (void *) wskaźnik, zwracany przez malloc () lub podobne, to nie, nie ma sposobu, aby określić, na jaką strukturę danych wskazuje wskaźnik, a tym samym, nie ma sposobu, aby określić jego rozmiar.

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

Wyjście:

SIZEOF_DAYS:  20
sizeof(days): 20
sizeof(*ptr): 20
sizeof(ptr):  4
 5
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-13 21:04:15

Nie ma magicznego rozwiązania. C nie jest językiem refleksyjnym. Obiekty nie wiedzą automatycznie, czym są.

Ale masz wiele do wyboru:

  1. oczywiście, Dodaj parametr
  2. zawiń wywołanie w makro i automatycznie Dodaj parametr
  3. Użyj bardziej złożonego obiektu. Definiuje strukturę, która zawiera tablicę dynamiczną, a także rozmiar tablicy. Następnie podaj adres struktury.
 4
Author: DigitalRoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-25 20:50:19

Możesz zrobić coś takiego:

int days[] = { /*length:*/5, /*values:*/ 1,2,3,4,5 };
int *ptr = days + 1;
printf("array length: %u\n", ptr[-1]);
return 0;
 3
Author: Tᴏᴍᴇʀ Wᴏʟʙᴇʀɢ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-22 11:42:34

Moim rozwiązaniem tego problemu jest zapisanie długości tablicy do tablicy struct jako meta-informacji o tablicy.

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

Ale musisz dbać o ustawienie odpowiedniej długości tablicy, którą chcesz przechowywać, ponieważ nie ma sposobu, aby sprawdzić tę długość, jak nasi przyjaciele masowo wyjaśnili.

 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-05 14:45:29

Nie, Nie można użyć sizeof(ptr), aby znaleźć rozmiar tablicy ptr, na który wskazuje.

Chociaż przydzielenie dodatkowej pamięci (większej niż rozmiar tablicy) będzie pomocne, jeśli chcesz zapisać długość w dodatkowej przestrzeni.

 1
Author: SKD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-15 11:22:40
int main() 
{
    int days[] = {1,2,3,4,5};
    int *ptr = days;
    printf("%u\n", sizeof(days));
    printf("%u\n", sizeof(ptr));

    return 0;
}

Wielkość dni [] wynosi 20, co nie jest liczbą elementów * wielkość jego typu danych. Podczas gdy rozmiar wskaźnika wynosi 4 bez względu na to, na co wskazuje. Ponieważ wskaźnik wskazuje na inny element przechowując jego adres.

 1
Author: this.shivi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-10 10:32:16
 #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 przechodzi do Rozmiar zmienna:

#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

Użycie to:

void main() {

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

        array1.array[i] *= 2;
    }
}
 0
Author: user3065147,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-14 22:58:40

W łańcuchach znajduje się znak '\0' na końcu, więc długość łańcucha można uzyskać za pomocą funkcji takich jak strlen. Problem z tablicą liczb całkowitych polega na tym, że nie można użyć żadnej wartości jako wartości końcowej, więc jednym z możliwych rozwiązań jest adresowanie tablicy i użycie jako wartości końcowej wskaźnika 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 );
}
 0
Author: baz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-15 00:34:23