Бросить исключение нулевого указателя [закрыто]

14

Ваша задача - создать исключение с нулевым указателем. То есть ваша программа должна принять значение, которое, как ожидается, будет ненулевым, и выдать исключение / ошибку или сбой, потому что значение равно нулю.

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

  • Вместо нуля вы можете использовать ноль, ни один, ничего, или любой другой эквивалент в вашем языке. Вы также можете использовать неопределенные, неинициализированные и так далее.
  • Проблема с вашим кодом должна заключаться в том, что переменная (удивительно) равна нулю, когда программа ожидает ненулевую переменную.
  • Ваша программа может реагировать на нулевое значение, выдавая исключение, выбрасывая ошибку, сбой или все, что она обычно делает, когда встречается неожиданное нулевое значение.

Это конкурс популярности, так что будьте умнее!

Ypnypn
источник
@Ourous Можете ли вы привести пример, чтобы показать, что вы имеете в виду?
Ипныпн
После просмотра, это скорее ошибка приведения, чем то, что вы ищете.
Οurous
Могу ли я использовать ошибку компилятора?
Марк
1
@Mark - это конкурс популярности; пусть сообщество решит. Я бы определенно проголосовал за ошибку компилятора.
11684
Я закрываю этот вопрос, потому что закулисные конкурсы не по теме для консенсуса сообщества .
Деннис

Ответы:

33

Джава

Давайте вычислим абсолютное значение числа. В Java есть Math.abs для этой цели, однако иногда используемое число может быть нулевым. Таким образом, нам нужен вспомогательный метод для решения этого случая:

public class NPE {
    public static Integer abs(final Integer x) {
        return x == null ? x : Math.abs(x);
    }

    public static void main(final String... args) {
        System.out.println(abs(null));
    }
}

Если x равен нулю, вернуть ноль, иначе используйте Math.abs ().
Код очень прост и понятен, и должен нормально работать ... верно?

Кстати, используя эту строку вместо:

return x == null ? null : Math.abs(x);

работает правильно. Я думал о том, чтобы сделать этот спойлер, но .. я думаю, что это так же озадачивает :)

Хорошо, объяснение:

Во-первых, Math.abs принимает не Integer, а int (есть также перегруженные методы для других числовых типов), а также возвращает int. В java int является примитивным типом (и не может быть нулевым), а Integer является его соответствующим классом (и может быть нулевым). Начиная с версии 5 Java, преобразование между int и Integer выполняется автоматически при необходимости. Таким образом, Math.abs может взять x, автоматически преобразовать его в int и вернуть int.

Теперь странная часть: когда тернарный оператор (? :) должен иметь дело с 2 выражениями, где одно имеет примитивный тип, а другое имеет соответствующий класс (такой как int и Integer), можно ожидать, что java преобразует примитив к классу (он же «бокс»), особенно когда тип, который ему нужен (здесь для возврата из метода), является ссылочным (классом) типом, а первое выражение также имеет ссылочный тип. Но Java делает с точностью до наоборот: он преобразует ссылочный тип в примитивный тип (он же «unboxing»). Так что в нашем случае он попытается преобразовать x в int, но int не может быть нулевым, поэтому он выбрасывает NPE.

Если вы скомпилируете этот код с использованием Eclipse, вы фактически получите предупреждение: «Доступ к нулевому указателю: это выражение типа Integer является нулевым, но требует автоматической распаковки»


/programming/7811608/java-npe-in-ternary-operator-with-autoboxing
/programming/12763983/nullpointerexception-through-auto-boxing-behavior-of-java -ternary-оператор

уйти, потому что SE это зло
источник
СПОЙЛЕРЫ Что происходит, когда х! = Ноль? (Я уже понял это.)
11684
@ 11684, когда x не нуль, он работает нормально
адицу ушел, потому что SE ЗЛО
Тогда я думаю, что я не знаю, как это работает. Если это работает, когда x не нуль, и я прав относительно того, почему это выбрасывает, двоеточие должно быть проанализировано в двух смыслах (который я не поместил бы в Спецификацию языка).
11684
@ 11684 не уверен, что ты имеешь в виду под "двумя чувствами", но я все равно добавил объяснение
адицу ушел, потому что SE ЗЛО
23

С

Каждый программист C сделал эту ошибку, по крайней мере, один раз.

#include <stdio.h>

int main(void) {
    int n=0;
    printf("Type a number : ");
    scanf("%d",n);
    printf("Next number is : %d",n+1);
    return 0;
}

Причина:

scanfпринимает указатель ( int *) в аргументе, здесь 0передается (NULL-указатель)

Fix:

scanf("%d",&n);

http://ideone.com/MbQhMM

Майкл М.
источник
1
Это не совсем так. scanfявляется переменной функцией, и аргумент неверного типа ( int) был задан, когда ожидалось int *. Поскольку C (я знаю, компиляторы могут обнаружить это в случае scanf) не может проверять типы в функции с переменным числом, это успешно компилируется. 0действительно является литералом нулевого указателя, но это относится только к непосредственно используемому литералу, не сохраняя его в переменной. nникогда не содержал нулевого указателя.
Конрад Боровски
Вам также необходимо проверить возвращаемое значение scanf, чтобы исправить эту функцию.
Дэвид Грейсон
1
@David Это не правильно без проверки возвращаемого значения, но это не приведет к сбою или вызову UB - n останется на 0. (Чтобы увидеть это, попробуйте перенаправить пустой файл как stdin.)
Riking
14

PHP

Этот укусил меня несколько раз.

<?php

class Foo {
  private $bar;

  function init() {
    $this->bar = new Bar();
  }

  function foo() {
    $this->bar->display_greeting(); // Line 11
  }
}

class Bar {
  function display_greeting() {
    echo "Hello, World!";
  }
}

$foo_instance = new Foo();
$foo_instance->init();
$foo_instance->foo();

Ожидаемый результат:

Hello, World!

Фактический результат:

Fatal error: Call to a member function display_greeting() on a non-object on line 11

иначе NullPointerException

Причина:

По умолчанию синтаксис конструктора обратно совместим с PHP 4.x, и поэтому функция fooявляется допустимым конструктором для класса Fooи, таким образом, переопределяет пустой конструктор по умолчанию. Подобного рода ошибок можно избежать, добавив пространство имен в ваш проект.

Примо
источник
9

CoffeeScript (на Node.js)

В CoffeeScript ?есть экзистенциальный оператор. Если переменная существует, она используется, в противном случае используется правая часть. Мы все знаем, что трудно писать переносимые программы. Например, печать в JavaScript указана ниже. Браузеры используют alert(или document.write), SpiderMonkey Оболочка использует printи Node.js использования console.log. Это безумие, но CoffeeScript помогает с этой проблемой.

# Portable printer of "Hello, world!" for CoffeeScript

printingFunction = alert ? print ? console.log
printingFunction "Hello, world!"

Давайте запустим это под Node.js. В конце концов, мы хотим убедиться, что наш скрипт работает.

ReferenceError: print is not defined
  at Object.<anonymous> (printer.coffee:3:1)
  at Object.<anonymous> (printer.coffee:3:1)
  at Module._compile (module.js:456:26)

Хм, с чего бы вы жаловались на это, когда alertтоже не определено?

По какой-то причине в CoffeeScript ?он остается ассоциативным, что означает, что он игнорирует неопределенные переменные только для левой стороны. Это нефиксировано, потому что, видимо, некоторые разработчики могут зависеть от? оставаясь ассоциативным .

Конрад Боровски
источник
8

Рубин

Найдите awk в ПУТИ клона Unix.

p = ENV['PATH'].split ':'

# Find an executable in PATH.
def find_exec(name)
  p.find {|d| File.executable? File.join(d, name)}
end

printf "%s is %s\n", 'awk', find_exec('awk')

К сожалению!

$ ruby21 find-awk.rb
find-awk.rb:5:in `find_exec': undefined method `find' for nil:NilClass (NoMethodError)
        from find-awk.rb:8:in `<main>'

Из ошибки мы знаем, что p.findназываетсяnil.find , так и pдолжно быть nil. Как это случилось?

В рубине def имеет собственную область видимости для локальных переменных и никогда не берет локальные переменные из внешней области видимости. Таким образом, назначение p = ENV['PATH'].split ':'не входит в сферу применения.

Обычно возникает неопределенная переменная NameError, но pэто особый случай. У Ruby есть глобальный метод с именем p. Так p.find { ... }становится вызов метода, вроде p().find { ... }. Когда pне имеет аргументов, он возвращает nil. (Код для игры в гольф используется pкак ярлык для nil.) Тогдаnil.find { ... } поднимается NoMethodError.

Я исправляю это, переписывая программу на Python.

import os
import os.path

p = os.environ['PATH'].split(':')

def find_exec(name):
    """Find an executable in PATH."""
    for d in p:
        if os.access(os.path.join(d, name), os.X_OK,
                     effective_ids=True):
            return d
    return None

print("%s is %s" % ('awk', find_exec('awk')))

Оно работает!

$ python3.3 find-awk.py 
awk is /usr/bin

Я, вероятно, хочу, чтобы это распечатать awk is /usr/bin/awk, но это другая ошибка.

kernigh
источник
7

C #

With()является методом расширения stringобъекта, который по сути является просто псевдонимом для string.Format().

using System;

namespace CodeGolf
{
    internal static class Program
    {
        private static void Main()
        {
            Console.WriteLine( "Hello, {0}!".With( "World" ) );
        }

        private static string With( this string format, params object[] args )
        {
            Type str = Type.GetType( "System.string" );
            MethodInfo fmt = str.GetMethod( "Format", new[] { typeof( string ), typeof( object[] ) } );

            return fmt.Invoke( null, new object[] { format, args } ) as string;
        }
    }
}

Выглядит хорошо, правда? Неправильно.

Type.GetType()требует полностью определенного, чувствительного к регистру имени типа. Проблема в том, что System.stringне существует; stringэто всего лишь псевдоним для фактического типа: System.String. Похоже, это должно сработать, но str.GetMethod()выкинет исключение, потому что str == null.

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

Тони Эллис
источник
Это здорово: D
Knerd
Это слишком очевидно. Читатель получить немедленный ключ , потому что есть два способа сделать то же самое .GetType()и typeof()и , таким образом , что приводит к неисправности , если результат не то же самое. Я думаю, что постер хотел пуленепробиваемый код, который не является.
ja72
Почему отражение будет использоваться в этом методе расширения? Очевидный код есть return string.Format(format, args);. Если бы понадобилось отражение (в другом случае использования), он использовал бы typeof(string)(ловит ошибки в регистре во время компиляции, без магической строки), а не статический GetTypeметод. Так что пример кажется "нереальным".
Джеппе Стиг Нильсен,
4

Unity3D

public GameObject asset;

Тогда вы забудете перетащить ресурс туда и BOOM, Unity взрывается. Бывает все время.

Фабрисио
источник
3
подождите, для разработки в unity3d требуется мышь?
Эйнасио
1
@Einacio, если вы хотите, чтобы все было проще, вы можете просто перетащить актив в переменную. Очень удобно. Но вы можете писать без этого, если хотите.
Fabricio
4

Рубин

Просто некоторый простой код, чтобы взять продукт массива.

number_array = [2,3,9,17,8,11,14]
product = 1
for i in 0..number_array.length do
  product *= number_array[i]
end
puts product

Две вещи здесь. Одним из них является то, что ..оператор диапазона является включающим . Таким образом, 0..xимеет х + 1 элементов и включает в себя х. Это означает, что мы превышаем границы массива. Другое дело, что когда вы делаете это в Ruby, это просто возвращает вас nilназад. Это очень весело, когда, скажем, ваша программа выбрасывает exceptдесять строк после ошибки.

Hovercouch
источник
Это слишком очевидно. Это как for (int i = 0; i <= arr.length; i++)в Java.
Коул Джонсон
3

Android

Я вижу, что это происходит слишком часто. Человек передает сообщение следующему действию (может быть, код состояния, данные карты и т. Д.) И в конечном итоге получает нулевое значение из Intent.

На первый взгляд это кажется довольно разумным. Только:

  • убедитесь, что сообщение не является нулевым
  • упаковать это в умысел
  • начать новую деятельность
  • получить намерение в новой деятельности
  • извлечь сообщение по тегу

В MenuActivity.java:

private void startNextActivity(String message){
    // don't pass a null!
    if(message == null)                        
        message = "not null";        

    // put message in bundle with tag "message"
    Bundle msgBundle = new Bundle();
    msgBundle.putString("message", message);   

    // pack it into a new intent
    Intent intent = new Intent(this, NextActivity.class);
    intent.putExtras(msgBundle);               
    startActivity(intent);
}

В NextActivity.java:

private void handleMessage(){
    // get Intent
    Intent received = getIntent();
    if(received == null){
        Log.d("myAppTag","no intent? how does this even happen?");
        finish();
    }
    // get String with tag "message" we added in other activity
    String message = received.getStringExtra("message");
    if(message.length() > 10){
        Log.d("myAppTag", "my message is too long! abort!");
        finish();
    }
    // handle message here, etc
    // ...
    // too bad we never GET here!
}

FWIW, то JavaDoc делает сказать , что Intent.getStringExtra(String)может вернуться null, но только если тег не был найден. Очевидно, я использую тот же тег, так что это должно быть что-то еще ...

Geobits
источник
2
Проблема с этим ответом состоит в том, что люди, не знакомые с разработкой для Android (такие как я), не смогут оценить это.
Джон Дворжак,
@JanDvorak Согласен, но ничего страшного. На сайте есть и другие ответы, которые я не очень ценю. :)
Geobits
3

С

Просто быстрое чтение из буфера, где программист даже был достаточно любезен, чтобы задокументировать потенциальную опасность!

char* buffer;
int main( void )
{
    ///!!WARNING: User name MUST NOT exceed 1024 characters!!\\\
    buffer = (char*) malloc( 1024 );
    printf("Please Input Your Name:");
    scanf("%s", buffer);
}

Очень простой и очевидный трюк, если вы ожидаете вредоносного кода. Конец комментария '\' экранирует символ новой строки, поэтому в буфере никогда не выделяется память. Затем он провалит сканирование, так как буфер будет иметь значение NULL (поскольку переменные области видимости файла инициализируются нулями в C).

Bilkokuya
источник
0

Нимрод

type TProc = proc (a, b: int): int

proc func1(a, b: int): int=a+b
proc func2(a, b: int): int=a-b

proc make_func(arg: int, target: var TProc)=
  if arg == 1:
    target = func1
  elif arg == 2:
    target = func2
  else:
    raise newException(EIO, "abc")

var f, f2: TProc

try:
  make_func(2, f)
  make_func(3, f2)
except EIO:
  discard

echo f(1, 2)
echo f2(3, 4)

То, что здесь происходит, немного сложно. В Nimrod процедуры по умолчанию инициализируются как nil. При первом make_funcвызове это удается. Второй, однако, создает исключение и оставляет его f2неинициализированным. Затем он вызывается, вызывая ошибку.

kirbyfan64sos
источник
0

C #

Это классика. Очень полезный метод FindStringRepresentationOfсоздаст новый экземпляр указанного параметра типа, а затем найти строковое представление этого экземпляра.

Конечно, будет бесполезно проверять nullсразу после создания нового экземпляра, поэтому я этого не сделал ...

static void Main()
{
  FindStringRepresentationOf<DateTime>();  // OK, "01/01/0001 00:00:00" or similar
  FindStringRepresentationOf<DateTime?>(); // Bang!
}

static string FindStringRepresentationOf<TNewable>() where TNewable : new()
{
  object goodInstance = new TNewable();
  return goodInstance.ToString();
}

Изменение objectобъявления локальной переменной в TNewable или в var(C # 3 и более поздние версии) устраняет проблему. Подсказка: бокс в Nullable<T>(aka T?) является аномальным в .NET Framework.

После исправления проблемы, как описано в невидимом тексте выше, также попробуйте goodInstance.GetType()(разница в том, что GetType(), в отличие от этого ToString(), не является виртуальной, поэтому они не могли overrideее в типе Nullable<>).

Джепп Стиг Нильсен
источник
0

C ++:

#include <iostream>
#include <cstring>
#include <vector>
#include <conio.h>
#include <string>

using namespace std;

class A
{
public:
    string string1;
    A()
    {
        string1 = "Hello World!";
    }
};
A *a_ptr;

void init()
{
    A a1;
    a_ptr = &a1;
}

int main()
{
    init();
    cout<<a_ptr->string1;
    _getch();
    return 0;
}

То, что вы ожидаете, это "Hello World!" быть напечатанным. Но вы увидите только мусор на экране.

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

wadf
источник
0

С

#define ONE 0 + 1

int main(void) {
    printf("1 / 1 = %d\n", 1 / ONE);
    return 0;
}

объяснение

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

nyuszika7h
источник