Eine Nullzeiger-Ausnahme auslösen [closed]


14

Ihre Aufgabe ist es, eine Nullzeiger-Ausnahme zu generieren. Das heißt, Ihr Programm muss einen Wert akzeptieren, von dem erwartet wird, dass er nicht null ist, und eine Ausnahme / einen Fehler oder einen Absturz auslösen, da der Wert null ist.

Darüber hinaus kann es beim Lesen des Codes nicht offensichtlich sein, dass der Wert null ist. Ihr Ziel ist es, dem Leser klar zu machen, dass der Wert nicht null ist, obwohl er es tatsächlich ist.

  • Anstelle von null können Sie auch nil, none, nothing oder was auch immer in Ihrer Sprache steht, verwenden. Sie können auch undefined, uninitialized usw. verwenden.
  • Das Problem mit Ihrem Code muss sein, dass die Variable (überraschenderweise) null ist, wenn das Programm eine Variable erwartet, die nicht null ist.
  • Ihr Programm kann auf die Null reagieren, indem es eine Ausnahme auslöst, einen Fehler auslöst, abstürzt oder was auch immer es normalerweise tut, wenn eine unerwartete Null auftritt.

Dies ist ein Beliebtheitswettbewerb, also sei schlau!


@Ourous Kannst du ein Beispiel geben, um zu zeigen, was du meinst?
Ypnypn

Nachdem Sie darüber nachgedacht haben, handelt es sich eher um einen Besetzungsfehler als um das, wonach Sie suchen.
Οurous

Darf ich einen Compiler-Bug verwenden?
Markieren Sie

1
@ Mark Es ist ein Beliebtheitswettbewerb; Lass die Community entscheiden. Ich würde definitiv für einen Compiler-Bug stimmen.
11684

Antworten:


33

Java

Berechnen wir den absoluten Wert einer Zahl. Java hat Math.abs für diesen Zweck, aber die Zahl, die wir verwenden, kann manchmal null sein. Wir brauchen also eine Hilfsmethode, um diesen Fall zu lösen:

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));
    }
}

Wenn x null ist, geben Sie null zurück, andernfalls verwenden Sie Math.abs ().
Der Code ist sehr einfach und klar und sollte gut funktionieren ... oder?

Verwenden Sie stattdessen diese Zeile:

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

funktioniert einwandfrei Ich habe darüber nachgedacht, dies zu einem Spoiler zu machen, aber ich denke, es ist genauso rätselhaft :)

Ok, eine Erklärung:

Erstens nimmt Math.abs keine Ganzzahl, sondern eine Ganzzahl (es gibt auch überladene Methoden für andere numerische Typen) und gibt auch eine Ganzzahl zurück. In Java ist int ein primitiver Typ (und kann nicht null sein) und Integer ist die entsprechende Klasse (und kann null sein). Seit Java Version 5 wird bei Bedarf automatisch eine Konvertierung zwischen int und Integer durchgeführt. So kann Math.abs x nehmen, automatisch in int konvertieren und ein int zurückgeben.

Nun der seltsame Teil: Wenn der ternäre Operator (? :) sich mit 2 Ausdrücken befassen muss, bei denen einer einen primitiven Typ hat und der andere eine entsprechende Klasse (wie int und Integer), würde man erwarten, dass Java das Primitive konvertiert Dies gilt insbesondere dann, wenn der Typ (hier für die Rückkehr von der Methode) der Referenztyp (Klassentyp) ist und der erste Ausdruck ebenfalls vom Referenztyp ist. Aber Java macht genau das Gegenteil: Es konvertiert den Referenztyp in den primitiven Typ (auch "Unboxing" genannt). In unserem Fall würde also versucht, x in int zu konvertieren, aber int kann nicht null sein, daher wird ein NPE ausgelöst.

Wenn Sie diesen Code mit Eclipse kompilieren, erhalten Sie eine Warnung: "Nullzeigerzugriff: Dieser Ausdruck vom Typ Integer ist null, erfordert jedoch das automatische Entpacken."


/programming/7811608/java-npe-in-ternary-operator-with-autoboxing
/programming/12763983/nullpointerexception-through-auto-boxing-behavior-of-java -terner-Operator



SPOILER Was passiert, wenn x! = Null ist? (Ich habe es bereits herausgefunden.)
11684

@ 11684 Wenn x nicht null ist, funktioniert es
einwandfrei

Dann glaube ich nicht zu wissen, wie das geht. Wenn es funktioniert, wenn x nicht null ist und ich recht habe, warum dies ausgelöst wird, muss der Doppelpunkt in zwei Richtungen analysiert werden (was ich in einer Sprachspezifikation nicht angeben würde).
11684

@ 11684 nicht sicher, was du mit "zwei Sinnen" meinst, aber trotzdem habe ich jetzt eine Erklärung hinzugefügt
aditsu

23

C

Jeder C-Programmierer hat diesen Fehler mindestens einmal gemacht.

#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;
}

Grund :

scanfNimmt einen Zeiger ( int *) im Argument, hier 0wird übergeben (NULL-Zeiger)

Fix:

scanf("%d",&n);

http://ideone.com/MbQhMM


1
Es ist nicht genau so. scanfist eine variable Funktion, und es wurde ein Argument des falschen Typs ( int) angegeben, als es erwartet wurde int *. Da C (ich weiß, Compiler können dies erkennen, wenn scanf) die Typen in variadic function nicht überprüfen kann, kompiliert dies glücklich. 0ist zwar ein Nullzeiger-Literal, dies gilt jedoch nur für das direkt verwendete Literal selbst, ohne es in einer Variablen zu speichern. nenthielt nie einen Nullzeiger.
Konrad Borowski

Sie müssen auch den Rückgabewert von scanf überprüfen, um diese Funktion zu beheben.
David Grayson

1
@ David Es ist nicht korrekt, ohne den Rückgabewert zu überprüfen, aber es wird nicht abstürzen oder UB aufrufen - n bleibt bei 0. (Um dies zu sehen, versuchen Sie, eine leere Datei als stdin umzuleiten.)
Riking

14

PHP

Dieser hat mich ein paar Mal gebissen.

<?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();

Erwartetes Ergebnis:

Hello, World!

Tatsächliche Ergebnis:

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

aka NullPointerException

Grund:

Standardmäßig ist die Konstruktorsyntax abwärtskompatibel mit PHP 4.x. Daher ist die Funktion fooein gültiger Konstruktor für die Klasse Foound überschreibt daher den leeren Standardkonstruktor. Diese Art von Fehler kann vermieden werden, indem Sie Ihrem Projekt einen Namespace hinzufügen.


9

CoffeeScript (auf Node.js)

In CoffeeScript ?ist dies ein existenzieller Operator. Wenn die Variable existiert, wird sie verwendet, andernfalls wird die rechte Seite verwendet. Wir alle wissen, dass es schwierig ist, tragbare Programme zu schreiben. In diesem Fall ist das Drucken in JavaScript nicht ausreichend. Browser verwenden alert(oder document.write), Spider Schale Verwendungen printund Node.js Verwendungen console.log. Das ist verrückt, aber das CoffeeScript hilft bei diesem Problem.

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

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

Lassen Sie uns dies unter Node.js ausführen. Schließlich möchten wir sicherstellen, dass unser Skript funktioniert.

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)

Ähm, warum würdest du dich darüber beschweren, wenn alertdas auch nicht definiert ist?

Aus irgendeinem Grund ist CoffeeScript linksassoziativ, ?dh undefinierte Variablen werden nur für die linke Seite ignoriert. Es ist unfixed, weil anscheinend einige Entwickler davon abhängen können? assoziativ gelassen werden .


8

Rubin

Finden Sie awk im PATH eines Unix-Klons.

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')

Hoppla!

$ 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>'

Aus dem Fehler wissen wir, dass das p.findheißt nil.find, so pmuss es seinnil . Wie ist es passiert?

Verfügt in Ruby defüber einen eigenen Bereich für lokale Variablen und nimmt niemals lokale Variablen aus dem äußeren Bereich. Also die Zuordnungp = ENV['PATH'].split ':' nicht im Umfang.

Eine undefinierte Variable verursacht normalerweise NameError, ist aber pein Sonderfall. Ruby hat eine globale Methode namens p. So p.find { ... }wird zum Beispiel ein Methodenaufruf p().find { ... }. Wenn pkeine Argumente vorhanden sind, wird zurückgegeben nil. (Code-Golfer verwenden pals Abkürzung für nil.) Dann nil.find { ... }erhöht NoMethodError.

Ich behebe es, indem ich das Programm in Python umschreibe.

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')))

Es klappt!

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

Ich möchte wahrscheinlich, dass es gedruckt wird awk is /usr/bin/awk, aber das ist ein anderer Fehler.


7

C #

With()ist eine Erweiterungsmethode für das stringObjekt, die im Wesentlichen nur ein Alias ​​für ist 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;
        }
    }
}

Sieht gut aus, oder? Falsch.

Type.GetType()erfordert einen vollständig qualifizierten Typnamen, bei dem die Groß- und Kleinschreibung beachtet wird. Das Problem ist, dass es System.stringdas nicht gibt; stringist nur ein Alias für den tatsächlichen Typ: System.String. Es sieht so aus, als sollte es funktionieren, aberstr.GetMethod() die Ausnahme auslösen, weil str == null.

Die meisten Leute, die ein wenig über die internen Besonderheiten der Sprache Bescheid wissen, werden das Problem wahrscheinlich recht schnell erkennen können, aber es ist immer noch etwas, das auf einen Blick leicht übersehen werden kann.


Das ist großartig: D
Knerd

Das ist etwas zu offensichtlich. Der Leser erhält sofort einen Hinweis, da es zwei Möglichkeiten gibt, dasselbe zu tun, .GetType()und dies typeof()zu einem Fehler führt, wenn das Ergebnis nicht dasselbe ist. Ich denke, das Plakat wollte einen Code, der kugelsicher aussieht, was nicht der Fall ist.
ja72

Warum sollte bei dieser Erweiterungsmethode Reflektion verwendet werden? Der offensichtliche Code ist return string.Format(format, args);. Wenn eine Reflektion erforderlich wäre (in einem anderen Anwendungsfall), würde man typeof(string)(fängt beim Kompilieren Schreibfehler ab, keine magische Zeichenfolge) verwenden, nicht die statische GetTypeMethode. Das Beispiel scheint also "unrealistisch".
Jeppe Stig Nielsen

4

Unity3D

public GameObject asset;

Dann vergisst du, das Asset dorthin zu ziehen und BOOM, Unity explodiert. Es passiert die ganze Zeit.


3
Warten Sie, die Entwicklung in Unity3D erfordert eine Maus?
Einacio

1
@Einacio Wenn Sie es einfacher haben möchten, können Sie ein Asset einfach per Drag & Drop auf eine Variable ziehen. Sehr praktisch. Sie können aber auch ohne Code arbeiten, wenn Sie möchten.
Fabricio

4

Rubin

Nur ein einfacher Code, um das Produkt eines Arrays zu nehmen.

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

Zwei Dinge hier. Zum einen ist der ..Bereichsoperator inklusive . Hat 0..xalso x + 1 Elemente und enthält x. Dies bedeutet, dass wir die Grenzen des Arrays überschreiten. Die andere Sache ist, dass, wenn Sie dies in Ruby tun, es Ihnen nur einen nilRücken gibt. Das macht eine Menge Spaß, wenn Ihr Programm beispielsweise exceptzehn Zeilen nach dem Fehler wirft .


Das ist irgendwie zu offensichtlich. Es ist wie for (int i = 0; i <= arr.length; i++)in Java.
Cole Johnson

3

Android

Ich sehe das zu oft. Eine Person übergibt eine Nachricht an die nächste Aktivität (möglicherweise einen Statuscode, Kartendaten usw.) und zieht am Ende eine Null aus derIntent .

Auf den ersten Blick scheint es ziemlich vernünftig. Gerade:

  • Stellen Sie sicher, dass die Nachricht nicht null ist
  • Pack es in die Absicht
  • Neue Aktivität starten
  • Absicht bekommen in neuen Aktivitäten
  • Nachricht nach Tag extrahieren

In 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);
}

In 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, das JavaDoc sagt , dass Intent.getStringExtra(String)das zurückkehren kann null, aber nur, wenn das Tag nicht gefunden wurde. Klar, ich benutze das gleiche Tag, also muss es etwas anderes sein ...


2
Das Problem mit dieser Antwort ist, dass Leute, die nicht mit Android-Entwicklung vertraut sind (wie ich), es nicht zu schätzen wissen.
John Dvorak

@ JanDvorak Einverstanden, aber keine große Sache. Es gibt andere Antworten auf der Website, die ich auch nicht voll und ganz schätze. :)
Geobits

3

C

Nur ein kurzer Blick in den Puffer, in dem der Programmierer sogar freundlich genug war, um die potenzielle Gefahr zu dokumentieren!

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);
}

Sehr einfacher und offensichtlicher Trick, wenn Sie bösartigen Code erwarten. Das Kommentarende '\' verschwindet aus der Newline, sodass dem Puffer niemals Speicher zugewiesen wird. Die Überprüfung schlägt dann fehl, da der Puffer NULL ist (da die Variablen des Dateibereichs in C mit Null initialisiert sind).


0

Nimrod

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)

Was hier passiert, ist etwas kompliziert. In Nimrod sind Prozeduren standardmäßig auf initialisiert nil. Beim ersten make_funcAufruf ist es erfolgreich. Der zweite löst jedoch eine Ausnahme aus und lässt f2nicht initialisiert. Es wird dann aufgerufen und verursacht einen Fehler.


0

C #

Das ist klassisch. Die sehr nützliche MethodeFindStringRepresentationOf erstellt eine neue Instanz des angegebenen Typparameters und ermittelt dann die Zeichenfolgendarstellung dieser Instanz.

Es wird sicherlich verschwenderisch sein, nullnach dem Erstellen einer neuen Instanz sofort danach zu suchen , also habe ich das nicht getan ...

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();
}

Durch Ändern objectder Deklaration der lokalen Variablen in TNewable oder in var(C # 3 und höher) wird das Problem behoben. Hinweis: Das Boxen von Nullable<T>(aka T?) ist in .NET Framework anomal.

Nachdem Sie das Problem wie im unsichtbaren Text oben beschrieben behoben haben, versuchen Sie es ebenfalls goodInstance.GetType()(der Unterschied besteht darin, dass es GetType()im Gegensatz zu ToString()nicht virtuell ist und sie es nicht overrideim Typ finden können 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;
}

Was Sie erwarten würden, ist "Hallo Welt!" um gedruckt zu werden. Aber Sie werden nur Müll auf dem Bildschirm sehen.

Hier wird die a1 zerstört, wenn das scope init () beendet ist. Also wird a_ptr, wie es auf a1 zeigt, Müll produzieren.


0

C

#define ONE 0 + 1

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

Erläuterung

Der Präprozessor berechnet nicht wirklich 0 + 1und ONEist daher wörtlich definiert als 0 + 1, was zu 1 / 0 + 1einer Division durch Null und einer Gleitkomma-Ausnahme führt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.