Как найти "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-битной системе)?

19 ответов

Решение

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

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

Ответ - нет."

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

Вы можете гуглить по причинам, чтобы с осторожностью относиться к таким макросам. Быть осторожен.

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

Существует чистое решение с шаблонами 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

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

Вот предложение, отличное от того, что было предоставлено до сих пор, которое будет работать: вместо этого передайте указатель на массив. Это предложение похоже на предложения стиля C++, за исключением того, что C не поддерживает шаблоны или ссылки:

#define ARRAY_SZ 10

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

Но это предположение несколько глупо для вашей проблемы, поскольку функция определена, чтобы точно знать размер передаваемого массива (следовательно, совсем не нужно использовать size of в массиве). Что он делает, тем не менее, предлагает некоторую безопасность типа. Это запретит вам передавать массив нежелательных размеров.

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

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

Для этого конкретного примера, да, есть, ЕСЛИ вы используете 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

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

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

Там нет волшебного решения. С не является рефлексивным языком. Объекты не знают автоматически, что они есть.

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

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

Мое решение этой проблемы заключается в сохранении длины массива в 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;
}

Но вам нужно позаботиться о том, чтобы установить правильную длину массива, который вы хотите сохранить, потому что нет способа проверить эту длину, как объяснили наши друзья.

Вот как я лично это делаю в своем коде. Я предпочитаю делать это как можно проще, но при этом иметь возможность получать нужные мне значения.

      typedef struct intArr {
    int size;
    int* arr; 
} intArr_t;

int main() {
    intArr_t arr;
    arr.size = 6;
    arr.arr = (int*)malloc(sizeof(int) * arr.size);

    for (size_t i = 0; i < arr.size; i++) {
        arr.arr[i] = i * 10;
    }

    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 указывает на.

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

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

#define array(type) struct { size_t size; type elem[0]; }

void *array_new(int esize, int ecnt)
{
    size_t *a = (size_t *)malloc(esize*ecnt+sizeof(size_t));
    if (a) *a = ecnt;
    return a;
}
#define array_new(type, count) array_new(sizeof(type),count)
#define array_delete free
#define array_foreach(type, e, arr) \
    for (type *e = (arr)->elem; e < (arr)->size + (arr)->elem; ++e)

int main(int argc, char const *argv[])
{
    array(int) *iarr = array_new(int, 10);
    array(float) *farr = array_new(float, 10);
    array(double) *darr = array_new(double, 10);
    array(char) *carr = array_new(char, 11);
    for (int i = 0; i < iarr->size; ++i) {
        iarr->elem[i] = i;
        farr->elem[i] = i*1.0f;
        darr->elem[i] = i*1.0;
        carr->elem[i] = i+'0';
    }
    array_foreach(int, e, iarr) {
        printf("%d ", *e);
    }
    array_foreach(float, e, farr) {
        printf("%.0f ", *e);
    }
    array_foreach(double, e, darr) {
        printf("%.0lf ", *e);
    }
    carr->elem[carr->size-1] = '\0';
    printf("%s\n", carr->elem);

    return 0;
}

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

/* http://stackru.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 '\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;
/*
 *    -- --
 *   -**-**-
 *   -$$$$$-
 *    -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 );
}
/* край */
 #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;
    }
}

В большинстве реализаций есть функция, которая сообщает вам зарезервированный размер для объектов, выделенных с помощью или calloc(), например, GNU имеет malloc_usable_size()

Однако это вернет размер перевернутого блока, который может быть больше, чем значение, заданное для malloc()/ realloc().


Существует популярный макрос, который вы можете определить для определения количества элементов в массиве (Microsoft CRT даже предоставляет ему OOB с именем _countof):

      #define countof(x) (sizeof(x)/sizeof((x)[0]))

Затем вы можете написать:

      int my_array[] = { ... some elements ... };
printf("%zu", countof(my_array)); // 'z' is correct type specifier for size_t

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

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

Другие вопросы по тегам