Другое событие NullPointerException
возникает, когда объявляется массив объектов, а затем сразу же пытается разыменовать его внутри.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Этот конкретный NPE можно избежать, если порядок сравнения отменяется ; а именно, использовать .equals
для гарантированного непустого объекта.
Все элементы внутри массива инициализируются их общим начальным значением ; для любого типа массива объектов, это означает, что все элементы null
.
Вы должны инициализировать элементы в массиве перед доступом или разыменованием их.
String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Адвокат языка вопрос. Hmkay.
Мой персональный top3:
искажения, нарушающее строгое правило
искажения:-)
, Редактирование Вот является небольшим примером, который делает это неправильно дважды:
(принимают 32 бита ints и прямой порядок байтов)
float funky_float_abs (float a)
{
unsigned int temp = *(unsigned int *)&a;
temp &= 0x7fffffff;
return *(float *)&temp;
}
, Что код пытается получить абсолютное значение плавания битовым жонглированием со знаковым битом непосредственно в представлении плавания.
Однако результатом создания указателя на объект путем кастинга от одного типа до другого не является допустимый C. Компилятор может предположить, что указатели на различные типы не указывают на тот же блок памяти. Это верно для всего вида указателей кроме пустоты*, и символ* (мыс знака не имеет значения).
В случае выше я делаю это дважды. Однажды для получения международного псевдонима для плавания a, и однажды преобразовать значение назад для плавания.
существует три допустимых способа сделать то же.
Использование символ или пустой указатель во время броска. Они всегда искажают к чему-либо, таким образом, они безопасны.
float funky_float_abs (float a)
{
float temp_float = a;
// valid, because it's a char pointer. These are special.
unsigned char * temp = (unsigned char *)&temp_float;
temp[3] &= 0x7f;
return temp_float;
}
Использование memcopy. Memcpy берет пустые указатели, таким образом, он вызовет искажение также.
float funky_float_abs (float a)
{
int i;
float result;
memcpy (&i, &a, sizeof (int));
i &= 0x7fffffff;
memcpy (&result, &i, sizeof (int));
return result;
}
третий допустимый путь: используйте объединения. Это явно не не определено начиная с C99:
float funky_float_abs (float a)
{
union
{
unsigned int i;
float f;
} cast_helper;
cast_helper.f = a;
cast_helper.i &= 0x7fffffff;
return cast_helper.f;
}
Используя макро-версии функций как "макс." или "isupper". Макросы оценивают их аргументы дважды, таким образом, Вы получите неожиданные побочные эффекты, когда Вы будете звонить макс. (++ я, j), или isupper (*p ++)
, Вышеупомянутое для стандарта C. В C++ в основном исчезли эти проблемы. Макс. функция является теперь шаблонной функцией.
упущение добавить static float foo();
в заголовочном файле, только получить исключения в операции с плавающей запятой, бросаемые, когда это возвратилось бы 0.0f;
Мое любимое неопределенное поведение состоит в том, что, если непустой исходный файл не заканчивается в новой строке, поведение не определено.
я подозреваю, что это верно, хотя тот никакой компилятор, который я буду когда-либо видеть, не рассматривал исходный файл по-другому согласно тому, является ли это завершенной новой строкой, кроме испустить предупреждение. Таким образом, это не действительно что-то, что удивит не зная программистов, кроме которых они могли бы быть удивлены предупреждением.
Так для подлинных проблем мобильности (которые главным образом являются зависящими от реализации, а не неуказанными или неопределенными, но я думаю, что это попадает в дух вопроса):
if (x+1 < x)
может быть оптимизирован как всегда ложь, когда x
подписал тип: см. -fstrict-overflow
опция в GCC). Действительно серьезные, которые могут быть удивительными даже на платформе, на которой Вы разработали, потому что поведение только частично не определено / неуказанный:
поточная обработка POSIX и модель памяти ANSI. Параллельный доступ к памяти также не определяется, как новички думают. энергозависимый не делает, какие новички думают. Порядок доступов памяти также не определяется, как новички думают. Доступы могут быть перемещенными через барьеры памяти в определенных направлениях. Когерентность кэша памяти не требуется.
Профильный код не так легок, как Вы думаете. Если Ваш испытательный шлейф не имеет никакого эффекта, компилятор может удалить часть или все это. встроенный не имеет никакого определенного эффекта.
И, как я думаю, Nils упомянул мимоходом:
Деление чего-то указателем на что-то. Просто не скомпилирует по некоторым причинам...:-)
result = x/*y;
Мой фаворит - это:
// what does this do?
x = x++;
Для ответа на некоторые комментарии это - неопределенное поведение согласно стандарту. Видя это, компилятору позволяют сделать что-либо до и включая формат Ваш жесткий диск. Посмотрите, например этот комментарий здесь . Точка не то, что Вы видите, что существует возможное разумное ожидание некоторого поведения. Из-за стандарта C++ и пути определяются точки последовательности, эта строка кода является на самом деле неопределенным поведением.
, Например, если бы мы имели x = 1
перед строкой выше, тогда чем допустимое закончилось бы быть впоследствии? Кто-то прокомментировал, что это должно быть
, x увеличен 1
, таким образом, мы должны видеть x == 2 впоследствии. Однако это не на самом деле верно, Вы найдете некоторые компиляторы, которые имеют x == 1 впоследствии, или возможно даже x == 3. Необходимо было бы пристально смотреть на сгенерированный блок для наблюдения, почему это могло бы быть, но различия происходят из-за базовой проблемы. По существу я думаю, что это вызвано тем, что компилятору позволяют оценить эти два оператора присвоений в любом порядке, который он любит, таким образом, он мог сделать x++
первый, или x =
сначала.
Другая проблема, с которой я встретился (который определяется, но определенно неожиданный).
символ является злым.
Компилятор не должен говорить Вам, что Вы вызываете функцию с неправильным количеством типов параметра параметров/несправедливости, если прототип функции не доступен.
EE, здесь просто обнаруженный, что a>>-2 является немного удручающим.
я кивнул и сказал им, что это не было естественно.
Обязательно всегда инициализируйте Ваши переменные перед использованием их! Когда я только что запустил с C, который вызвал меня много головных болей.