Как найти «sizeof» (указатель на массив)?

241

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

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

  • 79
    Я всегда использовал парены с sizeof - конечно, это выглядит как вызов функции, но я думаю, что это понятнее.
  • 17
    Почему бы и нет? Есть ли у вас что-то против лишних скобок? Я думаю, что с ними мне легче читать.
Показать ещё 6 комментариев
Теги:
arrays
pointers
sizeof

16 ответов

210
Лучший ответ

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

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

  • 9
    Прошу прощения за публикацию комментария так поздно, но если компилятор не знает, на что указывает указатель, как free узнает, сколько памяти нужно очистить? Я знаю, что эта информация хранится внутри для функций, таких как бесплатное использование. Итак, мой вопрос: почему компилятор может сделать это тоже?
  • 9
    @ viki.omega9, потому что free обнаруживает размер во время выполнения. Компилятор не может знать размер, потому что вы можете сделать массив другого размера в зависимости от факторов времени выполнения (аргументы командной строки, содержимое файла, фаза луны и т. Д.).
Показать ещё 10 комментариев
73

Ответ: "Нет".

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

  • 3
    Вот как реализованы паскали
  • 6
    и очевидно паскальские струны - вот почему Excel работает так быстро!
Показать ещё 1 комментарий
40

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

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

  • 10
    ARRAY_SIZE - это распространенная парадигма, используемая практическими программистами во всем мире.
  • 4
    Да, это общая парадигма. Вы все еще должны использовать его осторожно, хотя его легко забыть и использовать в динамическом массиве.
Показать ещё 5 комментариев
17

Существует чистое решение с шаблонами С++ без использования 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
  • 0
    Я никогда не видел обозначение T (&)[SIZE] . Можете ли вы объяснить, что это значит? Также вы можете упомянуть constexpr в этом контексте.
  • 1
    Это хорошо, если вы используете c ++ и у вас действительно есть переменная типа массива. Ни один из них не относится к вопросу: язык - это C, и OP хочет получить размер массива - простой указатель.
Показать ещё 4 комментария
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
4

Нет волшебного решения. C не является отражающим языком. Объекты автоматически не знают, что они собой представляют.

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

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

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

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

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

  • 0
    +1 за «Вы не можете получить эту информацию из затухшего значения указателя одного массива» и предоставляя обходной путь.
2
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 независимо от того, на что он указывает. Поскольку указатель указывает на другой элемент, сохраняя его адрес.

  • 1
    sizeof (ptr) - размер указателя, а sizeof (* ptr) - размер указателя, на который
2

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

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

1

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

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

0

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

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

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

0

некоторые функции, которые могут помочь решить проблему, но я должен упомянуть, что Зан Линкс и Пол Томблин верны в том, что они говорят.

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

int main() {

    int truInt = 12345679;
    int intLength = snprintf( NULL, 0, "%d", truInt );
    printf("size of truInt = %d: \n", intLength);

    int *ptr_truInt = &truInt;
    printf("size of ptr_truInt = %d: \n", (snprintf( NULL, 0, "%d", ptr_truInt[0] ) ) );

    int daysInt[] = {1,2,3,4,5,6,7,8,9};
    int *ptrIntDays = daysInt;
    printf("size of ptrDays = %d: \n\n", getIntPointerSize(ptrIntDays));

    char *stringPointer1 = "123456789";
    printf("size of stringPointer = %d: \n", getCharPointerSize(stringPointer));

    char daysChar[] = {'1','2','3','4','5','6','7','8','9','\0'};
    printf("size of daysChar = %u: \n", sizeof(daysChar));
    char *ptrDaysChar = daysChar;
    printf("size of ptrDaysChar2 = %u: \n", getCharPointerSize(ptrDaysChar));

    char daysChar2[] = "123456789";
    char *ptrDaysChar2 = daysChar2;
    printf("size of ptrDaysChar2 = %u: \n", getCharPointerSize(ptrDaysChar2));
    return 0;
}


int getIntPointerSize(unsigned pointer[]){
        int i=-1;
        while(pointer[++i] != '\0'){}
        return i;
 }

int getCharPointerSize(char *pointer){
    int i=-1;
    while(pointer[++i] != '\0'){}
    return i;
}
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 переходит к переменной 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;
    }
}
-2

Привет в строках есть символ '\0' в конце, поэтому можно получить размер строки с такими функциями, как 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 '\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 );
}
/* край */
  • 0
    Ваш код полон комментариев, но я думаю, что было бы проще, если бы вы добавили общее объяснение того, как это работает вне кода, как обычный текст. Можете ли вы отредактировать свой вопрос и сделать это? Спасибо!
  • 0
    Создание массива указателей на каждый элемент, чтобы вы могли линейно искать его для NULL является, вероятно, наименее эффективной альтернативой, которую можно представить, просто храня отдельный size напрямую. Особенно, если вы действительно используете этот дополнительный слой косвенности все время.
-2

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню