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

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

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

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

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

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

Уточнение : я знаю, что если я объявил массив в стеке вместо кучи (т. int arr[256]; « int arr[256]; »), оператор sizeof вернет 1024 (длина массива * sizeof (int)).

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

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

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

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

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

Нет, в Standard C ++ нет возможности сделать это.

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

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

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

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

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

Поэтому, чтобы дать вам количество «предметов»,

int * p_iToSize = arr – 4;

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

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

Обычный способ справиться с этим – либо использовать вектор

 int main() { std::vector 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); } 

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

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

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

  1. сохранить hash-таблицу указателя на размер
  2. написал его непосредственно возле вектора
  3. сделать что-то совершенно другое

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

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

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

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

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

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

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

Некоторая магия:

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

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

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

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

QuantumPete

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

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

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

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

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

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

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

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

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

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

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

 void foo(int* arr); int arr[100] = {0}; foo(arr+1); // Calls foo with a pointer to 100-1 elements. 

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

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

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

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

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

Параметры .

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

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

 char *ar = new char[100] 

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

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

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

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

@Dima,

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

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

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

Когда вы создаете указатели массива (Create wrapper with template to pointers), вы не можете, но когда вы создаете массив объекта, вы можете получить размер массива следующим образом:

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

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

Тело массива выглядит так:

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

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

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

Он печатает 100

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

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

  • Как узнать, указывает ли указатель на кучу или стек?
  • Динамическое распределение массива объектов
  • Как динамически распределять пространство памяти для строки и получать эту строку от пользователя?
  • Как я могу получить размер массива из указателя в C?
  • Чтение больших файлов в Java
  • Удаляет ли вызов деструктор?
  • std :: вектор и непрерывная память многомерных массивов
  • Методы classа, которые создают новые экземпляры
  • Что же случилось с использованием GC.Collect ()?
  • Предупреждения памяти iPhone OS. Что означают разные уровни?
  • Безопасно ли удалять указатель на пустоту?
  • Давайте будем гением компьютера.