Как найти «sizeof» (указатель на массив)? - PullRequest
277 голосов
/ 29 января 2009

Прежде всего, вот код:

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

Ответы [ 13 ]

241 голосов
/ 29 января 2009

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

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

76 голосов
/ 29 января 2009

Ответ: «Нет».

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

44 голосов
/ 29 января 2009

Для динамических массивов ( 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, намного безопаснее и проще в использовании.

17 голосов
/ 19 апреля 2012

Существует чистое решение с шаблонами 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
5 голосов
/ 14 апреля 2011

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

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

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

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

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

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

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

2 голосов
/ 22 июня 2018

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

int days[] = { /*length:*/5, /*values:*/ 1,2,3,4,5 };
int *ptr = days + 1;
printf("array length: %u\n", ptr[-1]);
return 0;
2 голосов
/ 10 июля 2016
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 независимо от того, на что он указывает. Потому что указатель указывает на другой элемент, сохраняя его адрес.

2 голосов
/ 05 октября 2015

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

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

...