Как найти 'sizeof' (указатель, указывающий на массив)?

во-первых, вот код:

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

    return 0;
}

есть ли способ узнать размер массива, что ptr указывает на (Вместо того, чтобы просто дать его размер, который составляет четыре байта в 32-битной системе)?

14 ответов


Нет, ты не можешь. Компилятор не знает, на что указывает указатель. Есть трюки, такие как завершение массива известным внеполосным значением, а затем подсчет размера до этого значения, но это не использует sizeof.

еще один трюк, упомянутый Зан, который должен где-то спрятать размер. Например, если вы динамически выделяете массив, выделите блок на один int больше, чем вам нужно, сохраните размер в первом int, и верните ptr+1 в качестве указателя на массив. Когда вам нужен размер, уменьшите указатель и посмотрите на скрытое значение. Просто не забудьте освободить весь блок, начиная с самого начала, а не только массив.


ответ: "Нет."

то, что программисты C делают, это хранить размер массива где-то. Это может быть частью структуры, или программист может обмануть немного и malloc() больше памяти, чем требуется, чтобы сохранить значение длины до начала массива.


для динамических массивов (Танос или C++ новая) вам нужно сохранить размер массива, как упоминалось другими, или, возможно, создать структуру диспетчера массивов, которая обрабатывает добавление, удаление, подсчет и т. д. К сожалению, 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;
}

вы можете google по причинам, чтобы опасаться макросов, как это. Быть осторожным.

Если возможно, c++ stdlib, такой как вектор, который намного безопаснее и проще использовать.


есть чистое решение с шаблонами C++, без использования sizeof (). Следующее 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

для этого конкретного примера, да, есть, если вы используете typedefs (см. ниже). Конечно, если вы делаете это таким образом, вы также можете использовать SIZEOF_DAYS, так как вы знаете, на что указывает указатель.

Если у вас есть указатель (void*), как возвращается malloc () или тому подобное, то нет способа определить, на какую структуру данных указывает указатель, и, следовательно, нет способа определить его размер.

#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 не является рефлексивным языком. Объекты автоматически не знают, что они такое.

но у вас есть много вариантов:

  1. очевидно, добавьте параметр
  2. оберните вызов в макрос и автоматически добавьте параметр
  3. используйте более сложный объект. Определите структуру, содержащую динамический массив, а также размер массива. Затем передайте адрес структуры.

Как указано во всех правильных ответах, вы не можете получить эту информацию только из распавшегося значения указателя массива. Если распавшийся указатель является аргументом, полученным функцией, то размер исходного массива должен быть предоставлен каким-то другим способом, чтобы функция узнала этот размер.

вот предложение, отличное от того,что было предоставлено до сих пор, которое будет работать: вместо этого передайте указатель на массив. Это предложение похоже на стиль 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 */

Если предполагается, что функция может работать на любой размер массива, то вы должны будете предоставить размер функции в качестве дополнительной информации.


мое решение этой проблемы-сохранить длину массива в массив struct в качестве мета-информации о массиве.

#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 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 независимо от того, на что он указывает. Потому что указатель указывает на другой элемент, сохраняя его адрес.


нет, вы не можете использовать sizeof(ptr) чтобы найти размер массива ptr указывает на.

хотя выделение дополнительной памяти (больше размера массива) будет полезно, если вы хотите сохранить длину в дополнительном пространстве.


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

вы можете сделать что-то вроде этого:

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

невозможно найти значение sizeof указателя, потому что компилятор не знает, как долго этот указатель poiting. Указатель sizeof должен быть 2 или 4 (в зависимости от компилятора).


привет в строках есть '' символ в конце, так что можно получить размер строки с такими функциями, как strlen проблема с целочисленным массивом, например, заключается в том, что u не может использовать любое значение в качестве конечного значения, поэтому одним из возможных решений является обращение к массиву и использование в качестве конечного значения NULL указатель

/* http://stackoverflow.com/questions/492384/how-to-find-the-sizeof-a-pointer-pointing-to-an-array */
#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 '' 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;
/*
 *    -- --
 *   -**-**-
 *   -$$$$$-
 *    -999-
 *     -^-
 *      -
 */
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 );
}
/* край */