Подтвердить что ты не робот

Можно ли программно определить размер массива С++? А если нет, то почему?

Этот вопрос был вдохновлен аналогичным вопросом: Как удалить [] "знать" размер массива операндов?

Мой вопрос немного другой: Есть ли способ определить размер массива С++ программно? И если нет, почему? Каждая функция, которую я видел, которая принимает массив, также требует целочисленного параметра, чтобы придать ему размер. Но, как указывал связанный вопрос, delete[] должен знать размер памяти, подлежащей освобождению.

Рассмотрим этот код С++:

int* arr = new int[256];
printf("Size of arr: %d\n", sizeof(arr));

Отпечатает "Size of arr: 4", который является только размером указателя. Было бы неплохо иметь некоторую функцию, которая печатает 256, но я не думаю, что один существует на С++. (Опять же, вопрос состоит в том, почему его не существует).

Разъяснение. Я знаю, что если бы я объявлял массив в стеке вместо кучи (т.е. "int arr[256];" ), то оператор sizeof возвращал бы 1024 (длина массива * sizeof ( целое)).

4b9b3361

Ответ 1

delete [] знает размер, который был выделен. Однако это знание находится во время выполнения или в диспетчере памяти операционной системы, что означает, что он не доступен компилятору во время компиляции. И sizeof() не является реальной функцией, он фактически вычисляется константой компилятором, что не может быть сделано для динамически распределенных массивов, размер которых неизвестен во время компиляции.

Также рассмотрим этот пример:


int *arr = new int[256];
int *p = &arr[100];
printf("Size: %d\n", sizeof(p));

Как компилятор знает, что такое размер p? Корень проблемы состоит в том, что массивы в C и С++ не являются первоклассными объектами. Они распадаются на указатели, и нет никакого способа, чтобы компилятор или сама программа знали, указывает ли указатель на начало фрагмента памяти, выделенного new, или на один объект или на какое-то место в середине фрагмента памяти, выделенного new.

Одной из причин этого является то, что C и С++ оставляют управление памятью программисту и операционной системе, и поэтому они не имеют сбора мусора. Реализация new и delete не является частью стандарта С++, потому что С++ предназначен для использования на разных платформах, которые могут управлять своей память совсем по-разному. Возможно, С++ может отслеживать все выделенные массивы и их размеры, если вы пишете текстовый процессор для окна, работающего на последнем процессоре Intel, но это может быть совершенно неосуществимо, когда вы пишете встроенную систему, DSP.

Ответ 2

Нет, в Standard С++ нет способа сделать это.

Нет истинной причины, по которой я не знаю. Вероятно, размер считался деталью реализации, и лучше всего не отображался. Обратите внимание, что когда вы говорите malloc (1000), нет гарантии, что возвращенный блок составляет 1000 байт, - только то, что он не менее 1000 байт. Скорее всего, это около 1020 (1K минус 4 байта для накладных расходов). В этом случае размер "1020" является важным для запоминания библиотеки времени выполнения. И, конечно, это изменилось бы между реализациями.

Вот почему Комитет по стандартам добавил std: vector < > , который отслеживает его точный размер.

Ответ 3

Ну, на самом деле есть способ определить размер, но он не "безопасен" и будет отличаться от компилятора от компилятора.... , поэтому он не должен использоваться вообще.

Когда вы выполните: int * arr = new int [256];

Не имеет значения 256, вам будет задано значение 256 * sizeof (int) для этого случая 1024, это значение будет храниться, вероятно, в (arr - 4)

Итак, чтобы дать вам количество "элементов"

int * p_iToSize = arr - 4;

printf ( "Количество элементов% d", * p_iToSize/sizeof (int));

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

Ответ 4

Общий способ справиться с этим - либо использовать вектор

int main()
{
   std::vector<int> v(256);
   printf("size of v is %i capacity is %i\n", sizeof(int) * v.size(), sizeof(int) * v.capacity());
}

или предопределить размер

const int arrSize = 256;
int main()
{
    int array[arrSize];
    printf("Size of array is %i", sizeof(int) * arrSize);
}

Ответ 5

С++ решил добавить новое, чтобы сделать typeafe malloc, чем новый должен знать как размер e числа элементов для вызова ctors, так и delete для вызова dtors. В первые дни вы должны фактически пройти, чтобы удалить числа, объекты, которые вы передали новым.

string* p = new string[5];
delete[5] p;

Однако они думали, что если использовать new <type> [], служебные данные числа были небольшими. Поэтому они решили, что новый [n] должен запомнить n и передать его для удаления. Существует три основных способа его реализации.

  • сохранить хэш-таблицу указателя на размер
  • написал его непосредственно возле вектора
  • сделать что-то совершенно другое

Возможно, возможно получить такой размер:

size_t* p = new size_t[10];
cout << p[-1] << endl;
// Or
cout << p[11] << endl;

Или, черт возьми, ни один из них.

Ответ 6

В зависимости от вашего приложения вы можете создать "контрольное значение" в конце вашего массива.

Дозорное значение должно иметь некоторое уникальное свойство.

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

Для простой строки C завершающий \0 пример примерного значения.

Ответ 7

Магия:

template <typename T, size_t S>
inline
size_t array_size(const T (&v)[S]) 
{ 
    return S; 
}

И так мы делаем это в С++ 11:

template<typename T, size_t S>
constexpr 
auto array_size(const T (&)[S]) -> size_t
{ 
    return S; 
}

Ответ 8

Это потому, что ваша переменная arr является только указателем. Он содержит адрес определенного места в памяти, ничего не зная об этом. Вы объявляете его int *, что дает компилятору некоторое указание на то, что делать, когда вы увеличиваете указатель. Кроме этого, вы могли бы указывать на начало или конец массива или в стек или в недопустимую память. Но я согласен с вами, не имея возможности вызвать sizeof, очень раздражает:)

QuantumPete

Ответ 9

Нет портативного способа определения размера динамически выделенного массива в С++ с учетом только его указателя. С++ сделан очень гибким и дает пользователю возможность. Например, стандарт не определяет, как распределители памяти должны работать, например. добавив заголовок требуемого размера. Не требуется заголовок, позволяющий значительно повысить гибкость.

В качестве одного примера рассмотрим строку, реализованную как массив char *. Обычно использовать указатели в середине массива для выделения подстрок. В качестве примера см. Функцию strtok в стандартной библиотеке C. Если некоторый заголовок должен быть встроен непосредственно перед каждым массивом, вам нужно будет удалить части массива перед подстрокой.

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

Шаблон std::vector - мой любимый способ сохранения размера массива, связанного с самим массивом.

C - это переносимый язык ассемблера с лучшим синтаксисом.

Ответ 10

Нет, нет никакого способа сделать это, вы должны следить за тем, насколько велика она внешне. Классы типа std::vector делают это для вас.

Ответ 11

Вы не можете, принципиально:

void foo(int* arr);

int arr[100] = {0};

foo(arr+1); // Calls foo with a pointer to 100-1 elements.

С++-массив - это не что иное, как совокупность объектов, которые хранятся в смежной области памяти. Поскольку между ними нет отверстий (прокладка внутри объектов), вы можете найти следующий элемент массива, просто поведя указатель. На уровне CPU это простая настройка. С++ только вставляет множитель sizeof (element).

Обратите внимание, что реализации могут выбрать реализацию "указателей жира", которые содержат границы массива. Они должны быть в два раза больше, так как вам нужно будет ссылаться на какой-то "дескриптор с привязкой к массиву". Как побочный эффект, на таких реализациях вы могли бы вызвать delete [] (1+new int[5]);

Ответ 12

К сожалению, это невозможно. В C и С++ программист должен помнить о длине массива, поскольку длина массива не хранится нигде. Delete [] и free() помнят размер выделенного блока, но они могут выделять больше памяти, чем запрошено, поэтому их внутренние структуры данных, сохраняющие размеры выделенных блоков памяти, могут не дать вам точный размер вашего массива.

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

Ответ 13

В общем, нет. Массивы на C и С++ - это только блоки памяти без приложенной информации о бухгалтерском учете. Не сохраняя длину массива в памяти и добавляя служебные данные для этого, в общем случае это невозможно.

Существует исключение для массивов, которые статически распределены. Например, если вы заявляете: int a[50], то sizeof(a) будет работать. Это возможно, потому что [50] является частью статического типа массива: он известен компилятору. sizeof интерпретируется во время компиляции.

Однако, если вы создаете указатель: int *p = a, то sizeof(p) вернет размер указателя по мере упоминания, а не размер массива, потому что компилятор не знает, на что указывает p.

Ответ 14

Теперь существует std:: array - эффективная обкатка времени компиляции вокруг массива с постоянным размером:

#include <array>

int main (int argc, char** argv)
{
    std::array<int, 256> arr;
    printf("Size of arr: %ld\n", arr.size());
}

Параметры <type, #elements>.

Вы также получаете несколько других тонкостей, таких как итераторы, empty() и max_size().

Ответ 15

Есть ли способ определить размер массива С++ программно? А если нет, то почему?

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

Ответ 16

@Dima,

Как компилятор узнает, что такое размер p?

Компилятор должен знать размер p; в противном случае он не может реализовать delete[]. Компилятору не нужно никому рассказывать, как это видно.

Для того, чтобы проверить это, сравните указатель, возвращаемый operator new[], указателю, возвращаемому new[].

Ответ 17

Компилятор не может знать, что

char *ar = new char[100] 

- массив из 100 символов, потому что он не создает фактический массив в памяти, он просто создает указатель на 100 неинициализированных байтов в памяти.

Если вы хотите узнать размер данного массива, просто используйте std::vector. std::vector - это лучший массив.

Ответ 18

Когда вы создаете указатели массива (создайте оболочку с шаблоном указателям), вы не можете, но когда вы создаете массив объекта, Вы можете получить размер массива следующим образом:

char* chars=new char[100];
printf("%d",*((int*)chars-1));

Функция delete[] должна деконструировать все объекты в ней. для этого ключевое слово new[] помещает количество элементов позади всего массива.

Тело массива таково:

int count;
ObjectType* data; //This value is returned when using new[]

Ответ 19

как я это делаю, деля размер массива на размер первого элемента

int intarray[100];
printf ("Size of the array %d\n", (sizeof(intarray) / sizeof(intarray[0]));

Он печатает 100

Ответ 20

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

В случае объявления и инициализации массива в момент создания вы можете его отсканировать, а затем сгенерировать число, которое не соответствует ни одному из элементов массива. Но если вы затем измените один из элементов, вы не узнаете, сохраняет ли этот элемент то же значение, что и последний элемент, поэтому вам нужно будет создать новый номер для хранения в последнем элементе. Пройдя все это, вы может также просто сохранить общее количество элементов в момент создания в переменной. И это, вероятно, будет иметь место, если вы используете только массив внутри функции.