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

Ответы

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

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

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

Ответ 11

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

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

Ответ 12

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

Ответ 13

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

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

Ответ 14

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

Ответ 15

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

Ответ 16

В строках в конце есть символ '\0', поэтому длину строки можно получить с помощью таких функций, как strlen. Например, проблема с целочисленным массивом состоит в том, что вы не можете использовать любое значение в качестве конечного значения, поэтому одним из возможных решений является обращение к массиву и использование в качестве конечного значения указателя 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 );
}