Lance une exception de pointeur nul [fermé]


14

Votre tâche consiste à générer une exception de pointeur nul. C'est-à-dire que votre programme doit accepter une valeur qu'il s'attend à être non nul et lever une exception / erreur ou un crash car la valeur est nulle.

De plus, il ne peut pas être évident à la lecture du code que la valeur est nulle. Votre objectif est de faire comprendre au lecteur que la valeur n'est pas nulle, même si elle l'est réellement.

  • Au lieu de null, vous pouvez utiliser nil, aucun, rien ou l'équivalent dans votre langue. Vous pouvez également utiliser non défini, non initialisé, etc.
  • Le problème avec votre code doit être que la variable est (étonnamment) nulle là où le programme attend une variable non nulle.
  • Votre programme peut répondre à la valeur null en lançant une exception, en lançant une erreur, en se bloquant ou quoi qu'il fasse normalement lorsqu'une valeur null inattendue est rencontrée.

Il s'agit d'un concours de popularité, alors soyez intelligent!


@Ourous Pouvez-vous donner un exemple pour montrer ce que vous voulez dire?
Ypnypn

Après l'avoir regardé, c'est plus une erreur de casting que ce que vous recherchez.
Οurous

Suis-je autorisé à utiliser un bogue du compilateur?
Mark

1
@Mark C'est un concours de popularité; laissez la communauté décider. Je voterais certainement pour un bug de compilation.
11684

Réponses:


33

Java

Calculons la valeur absolue d'un nombre. Java a Math.abs à cet effet, mais le nombre que nous utilisons peut parfois être nul. Nous avons donc besoin d'une méthode d'aide pour traiter ce cas:

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

Si x est nul, retournez null, sinon utilisez Math.abs ().
Le code est très simple et clair, et devrait fonctionner correctement ... non?

Soit dit en passant, en utilisant cette ligne à la place:

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

fonctionne correctement. J'ai pensé à en faire un spoiler, mais .. je pense que c'est tout aussi déroutant :)

Ok, une explication:

Tout d'abord, Math.abs ne prend pas un entier, mais un entier (il existe également des méthodes surchargées pour d'autres types numériques) et renvoie également un entier. En java, int est un type primitif (et ne peut pas être nul) et Integer est sa classe correspondante (et peut être nul). Depuis la version 5 de Java, une conversion entre int et Integer est effectuée automatiquement en cas de besoin. Math.abs peut donc prendre x, automatiquement converti en int, et retourner un int.

Maintenant, la partie étrange: lorsque l'opérateur ternaire (? :) doit gérer 2 expressions où l'une a un type primitif et l'autre a sa classe correspondante (comme int et Integer), on peut s'attendre à ce que java convertisse la primitive à la classe (aka "boxing"), surtout lorsque le type dont il a besoin (ici pour revenir de la méthode) est le type de référence (classe), et la première expression est également du type de référence. Mais java fait exactement le contraire: il convertit le type de référence en type primitif (aka "unboxing"). Donc dans notre cas, il essaierait de convertir x en int, mais int ne peut pas être nul, il lance donc un NPE.

Si vous compilez ce code à l'aide d'Eclipse, vous obtiendrez en fait un avertissement: "Accès au pointeur nul: cette expression de type Integer est nulle mais nécessite une décompression automatique"


/programming/7811608/java-npe-in-ternary-operator-with-autoboxing
/programming/12763983/nullpointerexception-through-auto-boxing-behavior-of-java -intermédiaire-opérateur



SPOILERS Que se passe-t-il lorsque x! = Null? (Je l'ai déjà compris.)
11684

@ 11684 lorsque x n'est pas nul, cela fonctionne
correctement

Ensuite, je pense que je ne sais pas comment cela fonctionne. Si cela fonctionne lorsque x n'est pas nul et que j'ai raison de savoir pourquoi cela se déclenche, les deux points doivent être analysés dans deux sens (ce que je ne mettrais pas dans une spécification de langue).
11684

@ 11684 je ne sais pas ce que vous voulez dire par "deux sens", mais de toute façon j'ai ajouté une explication maintenant
aditsu quitte parce que SE est EVIL

23

C

Chaque programmeur C a fait cette erreur, au moins une fois.

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

Raison:

scanfprend un pointeur ( int *) en argument, ici 0est passé (pointeur NULL)

Réparer:

scanf("%d",&n);

http://ideone.com/MbQhMM


1
Ce n'est pas exactement comme ça. scanfest une fonction variadique, et un argument de type incorrect ( int) a été donné, quand il s'y attendait int *. Parce que C (je sais, les compilateurs peuvent détecter cela en cas de scanf) ne peut pas vérifier les types dans la fonction variadic, cela se compile avec bonheur. 0est en effet un littéral de pointeur nul, mais cela ne s'applique qu'au littéral directement utilisé lui-même, sans le stocker dans une variable. nn'a jamais contenu de pointeur nul.
Konrad Borowski

Vous devez également vérifier la valeur de retour de scanf pour corriger cette fonction.
David Grayson

1
@David Ce n'est pas correct sans vérifier la valeur de retour, mais il ne plantera pas ou n'appellera pas UB - n restera à 0. (Pour voir cela, essayez de rediriger un fichier vide en tant que stdin.)
Riking

14

PHP

Celui-ci m'a mordu plusieurs fois.

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

Résultat attendu:

Hello, World!

Résultat actuel:

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

aka NullPointerException

Raison:

Par défaut, la syntaxe du constructeur est rétrocompatible avec PHP 4.x, et en tant que telle, la fonction fooest un constructeur valide pour la classe Foo, et remplace donc le constructeur vide par défaut. Ce type d'erreur peut être évité en ajoutant un espace de noms à votre projet.


9

CoffeeScript (sur Node.js)

Dans CoffeeScript, ?est un opérateur existentiel. Si la variable existe, elle est utilisée, sinon le côté droit est utilisé. Nous savons tous qu'il est difficile d'écrire des programmes portables. Par exemple, l'impression en JavaScript est sous spécifiée. Les navigateurs utilisent alert(ou document.write), Shell SpiderMonkey utilisations printet les utilisations Node.js console.log. C'est fou, mais le CoffeeScript aide à résoudre ce problème.

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

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

Exécutons cela sous Node.js. Après tout, nous voulons nous assurer que notre script fonctionne.

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)

Uhm, pourquoi vous plaindriez-vous de cela, alors que alertn'est pas non plus défini?

Pour une raison quelconque, dans CoffeeScript, ?est laissé associatif, ce qui signifie qu'il ignore les variables non définies uniquement pour le côté gauche. Ce n'est pas corrigé, car apparemment certains développeurs peuvent en dépendre? être laissé associatif .


8

Rubis

Trouvez awk dans le CHEMIN d'un clone 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')

Oups!

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

De l'erreur, nous savons que p.findappelé nil.find, pdoit donc être nil. Comment est-ce arrivé?

Dans Ruby, defa sa propre portée pour les variables locales et ne prend jamais de variables locales de la portée extérieure. L'affectation p = ENV['PATH'].split ':'n'est donc pas dans le champ d'application.

Une variable non définie provoque généralement NameError, mais pc'est un cas spécial. Ruby a une méthode globale nommée p. Devient p.find { ... }alors un appel de méthode, comme p().find { ... }. Quandp n'a pas d'arguments, il revient nil. (Code utilisé par les golfeurs pcomme raccourci nil.) nil.find { ... }Relance ensuite NoMethodError.

Je le corrige en réécrivant le programme en 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')))

Ça marche!

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

Je veux probablement qu'il s'imprime awk is /usr/bin/awk, mais c'est un bug différent.


7

C #

With()est une méthode d'extension à l' stringobjet, qui n'est essentiellement qu'un alias pour 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;
        }
    }
}

Ça a l'air bien, non? Faux.

Type.GetType()nécessite un nom de type complet et sensible à la casse. Le problème est qu'il System.stringn'existe pas; stringest juste un alias pour le réel Type: System.String. Il semble que cela devrait fonctionner, mais str.GetMethod()lèvera l'exception parce que str == null.

La plupart des gens qui connaissent un peu les spécificités internes de la langue seront probablement en mesure de repérer le problème assez rapidement, mais c'est toujours quelque chose qui est facilement oublié en un coup d'œil.


C'est génial: D
Knerd

C'est un peu trop évident. Le lecteur obtenir un indice immédiat parce qu'il ya deux façons de faire la même chose .GetType()et typeof()et conduisant ainsi à un défaut lorsque le résultat est le même. Je pense que l'affiche voulait un code à l'épreuve des balles qui ne l'est pas.
ja72

Pourquoi la réflexion serait-elle utilisée dans cette méthode d'extension? Le code évident est return string.Format(format, args);. Si la réflexion était nécessaire (dans un autre cas d'utilisation), on utiliserait typeof(string)(capture les erreurs de casse au moment de la compilation, pas de chaîne magique), pas la GetTypeméthode statique . L'exemple semble donc "irréaliste".
Jeppe Stig Nielsen

4

Unity3D

public GameObject asset;

Ensuite, vous oubliez de glisser-déposer l'actif là-bas et BOOM, Unity explose. Arrive tout le temps.


3
Attendez, le développement dans Unity3d nécessite une souris?
Einacio

1
@Einacio si vous voulez que les choses soient plus faciles, vous pouvez simplement faire glisser un élément vers une variable. Très utile. Mais vous pouvez coder sans cela si vous le souhaitez.
Fabricio

4

Rubis

Juste du code simple pour prendre le produit d'un tableau.

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

Deux choses ici. La première est que l' ..opérateur de plage est inclusif . Donc 0..xa x + 1 éléments et comprend x. Cela signifie que nous dépassons les limites du tableau. L'autre chose est que lorsque vous faites cela dans Ruby, cela vous donne juste un nildos. C'est très amusant quand, disons, votre programme lance exceptdix lignes après le bogue.


C'est un peu trop évident. C'est comme faire for (int i = 0; i <= arr.length; i++)en Java.
Cole Johnson

3

Android

Je vois cela arriver trop souvent. Une personne transmet un message à l'activité suivante (peut-être un code d'état, des données cartographiques, etc.) et finit par extraire un null duIntent .

À première vue, cela semble assez raisonnable. Juste:

  • assurez-vous que le message n'est pas nul
  • le mettre dans l'intention
  • commencer une nouvelle activité
  • faire preuve d'intention dans une nouvelle activité
  • extraire le message par balise

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

Dans 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, le JavaDoc fait dire que Intent.getStringExtra(String)peut revenir null, mais seulement si l'étiquette n'a pas été trouvé. Clairement, j'utilise la même balise, donc ça doit être autre chose ...


2
Le problème avec cette réponse est que les gens qui ne connaissent pas le développement Android (comme moi) ne pourront pas l'apprécier.
John Dvorak

@JanDvorak D'accord, mais ce n'est pas grave. Il y a d'autres réponses sur le site que je n'apprécie pas vraiment non plus. :)
Geobits

3

C

Juste une lecture rapide du tampon, où le programmeur a même eu la gentillesse de documenter le danger potentiel!

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

Astuce très simple et évidente si vous attendez du code malveillant. Le commentaire se terminant par «\» échappe à la nouvelle ligne, donc la mémoire tampon n'est jamais allouée. Il échouera alors le scanf, car le tampon sera NULL (car les variables de portée de fichier sont initialisées à zéro en C).


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)

Ce qui se passe ici est un peu compliqué. Dans Nimrod, les procédures sont initialisées par défaut à nil. Au premier make_funcappel, il réussit. Le second, cependant, lève une exception et laisse f2non initialisé. Il est ensuite appelé, provoquant une erreur.


0

C #

C'est classique. La méthode très utile FindStringRepresentationOfcréera une nouvelle instance du paramètre de type spécifié, puis trouvera la représentation sous forme de chaîne de cette instance.

Il sera sûrement inutile de vérifier nullimmédiatement après avoir créé une nouvelle instance, donc je ne l'ai pas fait ...

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

La modification objectde la déclaration de variable locale dans TNewable ou dans var(C # 3 et versions ultérieures) fait disparaître le problème. Astuce: la boxe de Nullable<T>(aka T?) est anormale dans le .NET Framework.

Après avoir résolu le problème comme décrit dans le texte invisible ci-dessus, essayez également goodInstance.GetType()(la différence étant que GetType(), contrairement à ToString(), il n'est pas virtuel, ils ne pouvaient donc pas le overridefaire dans le type 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;
}

Ce que vous attendez est "Bonjour tout le monde!" à imprimer. Mais vous ne verrez que des ordures à l'écran.

Ici, l'a1 est détruit lorsque la portée init () est terminée. Donc, a_ptr, comme il pointe vers a1, produira des ordures.


0

C

#define ONE 0 + 1

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

Explication

Le préprocesseur ne calcule pas réellement 0 + 1, il ONEest donc littéralement défini comme 0 + 1, résultant en 1 / 0 + 1, qui est une division par zéro, et entraîne une exception à virgule flottante.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.