Определить размер массива C++ программно?

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

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

61
задан jww 3 March 2019 в 20:21
поделиться

14 ответов

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++ отслеживать все выделенные массивы и их размеры, если Вы пишете текстовой процессор для поля окон, работающего на последнем Intel CPU, но может быть абсолютно невозможно, когда Вы пишете встроенную систему, работающую на DSP.

66
ответ дан Dima 24 November 2019 в 17:13
поделиться

там какой-либо способ определить размер массива C++ программно? И в противном случае почему?

  1. нет, если Вы не отслеживаете его сами.
  2. , поскольку, если компилятор не должен говорить никому помимо себя о той информации, что это ограничивает компилятор меньше. Желательно ли это или не предоставлено право дебатировать.
0
ответ дан Tim Cooper 24 November 2019 в 17:13
поделиться

@Dima,

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

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

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

0
ответ дан Tim Cooper 24 November 2019 в 17:13
поделиться

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

char *ar = new char[100] 

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

, Если Вы хотите знать, размер данного массива просто использует станд.:: вектор. станд.:: вектор является лучшим массивом просто.

0
ответ дан SMeyers 24 November 2019 в 17:13
поделиться

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

1
ответ дан Tim Cooper 24 November 2019 в 17:13
поделиться

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

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

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

1
ответ дан James Rose 24 November 2019 в 17:13
поделиться

Вы не можете, существенно:

void foo(int* arr);

int arr[100] = {0};

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

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

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

1
ответ дан Tim Cooper 24 November 2019 в 17:13
поделиться

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

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

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

станд.:: векторный шаблон является моим любимым способом сохранить размер массива связанным с самим массивом.

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

2
ответ дан Mr Fooz 24 November 2019 в 17:13
поделиться

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

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
ответ дан Doug T. 24 November 2019 в 17:13
поделиться

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

QuantumPete

2
ответ дан QuantumPete 24 November 2019 в 17:13
поделиться

Нет, нет никакого способа сделать это в Стандартном C++.

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

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

18
ответ дан James Curran 24 November 2019 в 17:13
поделиться

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

, Когда Вы делаете: интервал* прибытие = новый интервал [256];

Эти 256 не важны, Вам дадут 256*sizeof (интервал) принятие для этого случая 1024, это значение будет сохранено, вероятно, в (прибытие - 4)

Так, чтобы дать Вам количество "объектов"

интервал* p_iToSize = прибытие - 4;

printf ("Количество объектов %d", *p_iToSize / sizeof (интервал));

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

16
ответ дан João Augusto 24 November 2019 в 17:13
поделиться

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

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

1
ответ дан Tamas Czinege 24 November 2019 в 17:13
поделиться

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

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

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

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

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

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

Или ад ни один из тех.

3
ответ дан Mykelyk 24 November 2019 в 17:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: