Есть ли способ определить размер массива C++ программно? А если нет, то почему?
этот вопрос был вдохновлен аналогичным вопросом: Как удалить[] "знать" размер массива операндов?
мой вопрос немного иначе: есть ли способ определить размер массива c++ программно? А если нет, то почему? каждая функция, которую я видел, которая принимает массив, также требует целочисленного параметра, чтобы дать ему размер. Но, как указал связанный вопрос,delete[] должен знать размер памяти, чтобы быть освободившему.
рассмотрим этот код C++:
int* arr = new int[256];
printf("Size of arr: %dn", sizeof(arr));
это выводит "Size of arr: 4", который является просто размером указателя. Было бы неплохо иметь некоторую функцию, которая печатает 256, но я не думаю, что она существует в C++. (Опять же, часть вопроса заключается в том, почему он не существует.)
уточнение: Я знаю, что если я объявил массив на стеке вместо кучи (т. е. "int arr[256];"), что sizeof оператор вернет 1024 (длина массива * sizeof (int)).
20 ответов:
delete []Не знаю размер, который был выделен. Однако эти знания хранятся в среде выполнения или в диспетчере памяти операционной системы, что означает, что они недоступны компилятору во время компиляции. Иsizeof()не является реальной функцией, она фактически оценивается в константу компилятором, что он не может сделать для динамически выделенных массивов, размер которых не известен во время компиляции.кроме того, рассмотрим это пример:
int *arr = new int[256]; int *p = &arr[100]; printf("Size: %d\n", sizeof(p));как компилятор узнает, что размер
p- это? Корень проблемы заключается в том, что массивы в C и C++ не являются объектами первого класса. Они распадаются на указатели, и компилятор или сама программа не могут узнать, указывает ли указатель на начало куска памяти, выделенногоnew, или к одному объекту, или к какому-то месту в середине куска памяти, выделенногоnew.одной из причин этого является то, что C и C++ оставляют управление памятью программисту и операционной системе, поэтому у них также нет сборки мусора. Реализация
newиdeleteне является частью стандарта C++, потому что C++ предназначен для использования на различных платформах, которые могут управлять своей памятью очень по-разному. Возможно, можно позволить C++ отслеживать все выделенные массивы и их размеры, если вы пишете текстовый процессор для окна windows, работающего на последнем процессоре Intel, но это может быть совершенно невозможно, когда вы пишете встроенную систему, работающую на DSP.
нет, это невозможно сделать в стандартном 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, new, независимо от того, что перед блоком памяти continuos, который вы получаете, также выделяется место, зарезервированное с некоторой информацией о блоке памяти, который вам был предоставлен.
общий способ справиться с этим-либо использовать вектор
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); }
C++ решил добавить новый, чтобы сделать typesafe malloc, чем Новый должен знать оба размера e числа элементов для вызова ctors, поэтому удалите для вызова dtors. В первые дни вы должны фактически пройти, чтобы удалить номера объектов, которые вы передали новым.
string* p = new string[5]; delete[5] p;однако они думали, что если использовать new
[] накладные расходы числа были невелики. Поэтому они решили, что new[n] должен запомнить n и передать его для удаления. Существует три основных способа реализации оно.
- держите хэш-таблицу указателя на размер
- написал его прямо возле вектора
- сделать что-то совершенно другое
возможно, можно получить такой размер:
size_t* p = new size_t[10]; cout << p[-1] << endl; // Or cout << p[11] << endl;или, черт возьми, ни один из них.
в зависимости от вашего приложения, вы можете создать "значение sentinel" в конце вашего массива.
значение sentinel должно иметь какое-то уникальное свойство.
затем вы можете либо обработать массив (или выполнить линейный поиск) для значения sentinel, считая по ходу. Как только вы достигнете значения sentinel, у вас есть количество массивов.
для простой строки C окончание \0 является примером значения sentinel.
магия:
template <typename T, size_t S> inline size_t array_size(const T (&v)[S]) { return S; }и вот как мы это делаем в C++11:
template<typename T, size_t S> 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, которые в основном массивы, завернутые в класс с некоторыми вспомогательными функциями, сохраняют длину массива, поэтому, если вам действительно нужна эта функциональность, вы можете просто использовать векторы.
В общем, нет. Массивы в C и C++ - это просто блоки памяти, к которым не прилагается бухгалтерская информация. Без сохранения длины массива в памяти и добавления накладных расходов это невозможно в общем случае.
существует исключение для массивов, которые статически выделены. Например, если вы объявите:
int a[50]затемsizeof(a)будет работать. Это возможно потому, что [50] является частью статического типа массива: он известен компилятору. sizeof is интерпретируется во время компиляции.однако, если вы создадите указатель:
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++ массив-это не более чем набор объектов, которые хранятся в непрерывной области памяти. Поскольку между ними нет отверстий (обивка внутри объекты), вы можете найти следующий элемент массива, просто incerementing указатель. На уровне процессора это простая настройка. C++ вставляет только множитель sizeof(element).
обратите внимание, что реализации могут выбрать для реализации "fat указатели", которые содержат границы массива. Они должны быть в два раза больше, так как вам нужно будет связать с каким-то "дескриптором, связанным с массивом". В качестве побочного эффекта, на таких реализациях вы могли бы быть в состоянии вызвать
delete [] (1+new int[5]);
нет, нет никакого способа сделать это, вы должны отслеживать, насколько он велик внешне. Классы, как
std::vectorсделать это для вас.
вот тут 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().
компилятор не может знать, что
char *ar = new char[100]- это массив из 100 символов, потому что он не создает фактический массив в памяти, он просто создает указатель на 100 неинициализированных байтов в памяти.
Если вы хотите узнать размер данного массива, просто используйте std::vector. std:: vector-это просто лучший массив.
есть ли способ определить размер массива c++ программно? А если нет, то почему?
- нет, если вы не следите это сами.
- потому что если компилятор не должен никому, кроме себя, рассказывать об этой информации, он меньше ограничивает компилятор. Является ли это желательным или нет, это дискуссия.
@Dima,
как компилятор узнает, что размер p?
компилятор должен знать размер p; в противном случае он не может реализовать
delete[]. Компилятору не нужно никому рассказывать, как он это выясняет.для забавного способа проверить это, сравните указатель, возвращенный
operator new[]к указателю, возвращенномуnew[].
при создании указателей массива (создать оболочку с шаблоном для указателей) вы не можете, но при создании массива объекта, Вы можете получить размер массива следующим образом:
char* chars=new char[100]; printf("%d",*((int*)chars-1));The
delete[]функция должна деконструировать все объекты в нем. чтобы сделать это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
вы можете просто создать дополнительный элемент массива, а затем применить наиболее маловероятное число, которое будет храниться в массиве. Затем вы можете определить количество элементов через некоторую функцию, передав это число.
в случае объявления и инициализации массива при его создании, вы можете затем проверить его и затем генерировать число, которое не соответствует ни одному из элементов массива. Но если вы затем измените один из элементов, вы не будете знать, если это элемент хранит то же значение, что и последний элемент, поэтому вам придется создать новое число для хранения в последнем элементе.. Пройдя через все это, вы можете также просто сохранить общее количество элементов в момент создания в переменной. И это, вероятно, будет иметь место, если вы используете только массив внутри функции.
Comments