Melempar pengecualian null-pointer [ditutup]


14

Tugas Anda adalah menghasilkan pengecualian null-pointer. Yaitu, program Anda harus menerima nilai yang diharapkannya bukan nol, dan melemparkan pengecualian / kesalahan atau macet karena nilainya nol.

Selain itu, tidak dapat dengan jelas membaca kode bahwa nilainya nol. Tujuan Anda adalah membuatnya tampak jelas bagi pembaca bahwa nilainya bukan nol, meskipun sebenarnya demikian.

  • Alih-alih nol, Anda dapat menggunakan nihil, tidak ada, tidak ada, atau apa pun yang setara dalam bahasa Anda. Anda juga dapat menggunakan undefined, uninitialized, dan sebagainya.
  • Masalah dengan kode Anda harus bahwa variabel tersebut (secara mengejutkan) adalah nol di mana program mengharapkan variabel yang bukan nol.
  • Program Anda dapat merespons nol dengan melempar pengecualian, melempar kesalahan, menabrak, atau apa pun yang biasanya dilakukannya ketika null yang tidak terduga ditemukan.

Ini adalah kontes popularitas, jadi cerdaslah!


@Ourous Bisakah Anda memberikan contoh untuk menunjukkan apa yang Anda maksud?
Ypnypn

Setelah melihatnya, ini lebih merupakan kesalahan pemain dari apa yang Anda cari.
Kamis

Apakah saya diizinkan menggunakan bug kompiler?
Markus

1
@ Mark Ini adalah kontes popularitas; biarkan masyarakat yang memutuskan. Saya pasti akan memilih bug kompiler.
11684

Jawaban:


33

Jawa

Mari kita hitung nilai absolut suatu angka. Java memiliki Math.ab untuk tujuan ini, namun angka yang kita gunakan terkadang nol. Jadi kita membutuhkan metode pembantu untuk menangani kasus itu:

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

Jika x adalah nol, kembalikan nol, kalau tidak gunakan Math.abs ().
Kode ini sangat sederhana dan jelas, dan harusnya berfungsi dengan baik ... benar?

Omong-omong, gunakan baris ini sebagai gantinya:

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

bekerja dengan benar. Saya berpikir untuk membuat ini spoiler, tapi .. Saya pikir itu sama membingungkannya :)

Ok, sebuah penjelasan:

Pertama, Math.abs tidak mengambil Integer, tetapi sebuah int (ada juga metode kelebihan beban untuk tipe numerik lainnya) dan juga mengembalikan sebuah int. Dalam java, int adalah tipe primitif (dan tidak bisa nol) dan Integer adalah kelas yang sesuai (dan bisa nol). Sejak java versi 5, konversi antara int dan Integer dilakukan secara otomatis ketika diperlukan. Jadi Math.abs dapat mengambil x, secara otomatis dikonversi ke int, dan mengembalikan int.

Sekarang bagian yang aneh: ketika operator ternary (? :) harus berurusan dengan 2 ekspresi di mana satu memiliki tipe primitif dan yang lainnya memiliki kelas yang sesuai (seperti int dan Integer), orang akan berharap bahwa java akan mengkonversi primitif ke kelas (alias "tinju"), terutama ketika jenis yang dibutuhkan (di sini untuk kembali dari metode) adalah tipe referensi (kelas), dan ekspresi pertama juga dari jenis referensi. Tapi java melakukan yang sebaliknya: ia mengubah tipe referensi ke tipe primitif (alias "unboxing"). Jadi dalam kasus kami, ia akan mencoba untuk mengkonversi x ke int, tetapi int tidak bisa nol, sehingga melempar NPE.

Jika Anda mengkompilasi kode ini menggunakan Eclipse, Anda benar-benar akan mendapatkan peringatan: "Null pointer access: Ungkapan tipe Integer ini nol tetapi membutuhkan pembatalan otomatis"


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



SPOILER Apa yang terjadi ketika x! = Null? (Saya sudah menemukan
jawabannya

@ 11684 ketika x bukan nol, itu berfungsi dengan baik
aditsu berhenti karena SE adalah JAHAT

Maka saya pikir saya tidak tahu bagaimana ini bekerja. Jika itu berfungsi ketika x bukan nol dan saya benar tentang mengapa ini melempar, usus besar harus diurai dalam dua pengertian (yang saya tidak akan dimasukkan ke dalam Spesifikasi Bahasa).
11684

@ 11684 tidak yakin apa yang Anda maksud tentang "dua indera", tapi bagaimanapun saya menambahkan penjelasan sekarang
aditsu berhenti karena SE adalah JAHAT

23

C

Setiap programmer C membuat kesalahan ini, setidaknya sekali.

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

Alasan:

scanfmengambil pointer ( int *) dalam argumen, 0ini diteruskan (NULL-pointer)

Memperbaiki:

scanf("%d",&n);

http://ideone.com/MbQhMM


1
Tidak persis seperti itu. scanfadalah fungsi variadik, dan argumen tipe salah ( int) diberikan, saat itu diharapkan int *. Karena C (saya tahu, kompiler dapat mendeteksi hal ini jika terjadi scanf) tidak dapat memeriksa tipe dalam fungsi variadic, ini dengan senang hati mengkompilasi. 0memang null pointer literal, tetapi ini hanya berlaku untuk literal yang langsung digunakan itu sendiri, tanpa menyimpannya dalam variabel. ntidak pernah berisi pointer nol.
Konrad Borowski

Anda juga perlu memeriksa nilai balik scanf untuk memperbaiki fungsi ini.
David Grayson

1
@ David Itu tidak benar tanpa memeriksa nilai kembali, tetapi tidak akan crash atau memanggil UB - n akan tetap di 0. (Untuk melihat ini terjadi, coba redirect file kosong sebagai stdin.)
Riking

14

PHP

Yang ini telah menggigit saya beberapa kali.

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

Hasil yang diharapkan:

Hello, World!

Hasil aktual:

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

alias NullPointerException

Alasan:

Secara default, sintaks konstruktor mundur kompatibel dengan PHP 4.x, dan dengan demikian, fungsi fooadalah konstruktor yang valid untuk kelas Foo, dan dengan demikian menimpa konstruktor kosong default. Kesalahan semacam ini dapat dihindari dengan menambahkan namespace ke proyek Anda.


9

CoffeeScript (di Node.js)

Dalam CoffeeScript, ?adalah operator eksistensial. Jika variabel ada, itu digunakan, jika tidak sisi kanan sedang digunakan. Kita semua tahu bahwa sulit untuk menulis program portabel. Contohnya, pencetakan dalam JavaScript berada di bawah yang ditentukan. Browser menggunakan alert(atau document.write), menggunakan shell SpiderMonkey print, dan Node.js menggunakan console.log. Ini gila, tetapi CoffeeScript membantu mengatasi masalah ini.

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

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

Mari kita jalankan ini di bawah Node.js. Lagi pula, kami ingin memastikan skrip kami berfungsi.

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, mengapa Anda mengeluh tentang itu, padahal alertjuga tidak didefinisikan?

Untuk beberapa alasan, dalam CoffeeScript, ?asosiatif kiri, yang berarti mengabaikan variabel yang tidak terdefinisi hanya untuk sisi kiri. Itu tidak tetap, karena ternyata beberapa pengembang mungkin bergantung pada? ditinggal asosiatif .


8

Rubi

Temukan awk di PATH klon 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')

Ups!

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

Dari kesalahan, kita tahu yang p.finddipanggil nil.find, jadi pharus nil. Bagaimana ini bisa terjadi?

Di Ruby, defmemiliki cakupan sendiri untuk variabel lokal, dan tidak pernah mengambil variabel lokal dari cakupan luar. Dengan demikian penugasan p = ENV['PATH'].split ':'tidak dalam ruang lingkup.

Variabel tidak terdefinisi biasanya menyebabkan NameError, tetapi pmerupakan kasus khusus. Ruby memiliki nama metode global p. Jadi p.find { ... }menjadi pemanggilan metode, seperti p().find { ... }. Ketika ptidak memiliki argumen, ia kembali nil. (Kode pegolf digunakan psebagai jalan pintas untuk nil.) Kemudian nil.find { ... }naikkan NoMethodError.

Saya memperbaikinya dengan menulis ulang program dengan 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')))

Berhasil!

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

Saya mungkin ingin mencetak awk is /usr/bin/awk, tetapi itu adalah bug yang berbeda.


7

C #

With()adalah metode ekstensi untuk stringobjek, yang pada dasarnya hanya alias untuk 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;
        }
    }
}

Terlihat bagus, bukan? Salah.

Type.GetType()membutuhkan nama jenis yang sepenuhnya memenuhi syarat, peka huruf besar-kecil. Masalahnya adalah itu System.stringtidak ada; stringhanya sebuah alias untuk yang sebenarnya jenis: System.String. Sepertinya itu harus berfungsi, tetapi str.GetMethod()akan membuang pengecualian karenastr == null .

Sebagian besar orang yang tahu sedikit tentang spesifikasi internal bahasa mungkin akan dapat menemukan masalah dengan cukup cepat, tetapi itu masih sesuatu yang mudah dilewatkan sekilas.


Itu bagus sekali: D
Knerd

Ini agak terlalu jelas. Pembaca mendapatkan petunjuk langsung karena ada dua cara untuk melakukan hal yang sama .GetType()dan typeof()dan sehingga mengarah ke kesalahan ketika hasilnya tidak sama. Saya pikir poster itu ingin kode yang mencari bukti peluru yang tidak.
ja72

Mengapa refleksi digunakan dalam metode ekstensi ini? Kode yang jelas adalah return string.Format(format, args);. Jika refleksi diperlukan (dalam kasus penggunaan lain), orang akan menggunakan typeof(string)(menangkap kesalahan casing pada waktu kompilasi, tidak ada string ajaib), bukan GetTypemetode statis . Jadi contohnya tampak "tidak realistis".
Jeppe Stig Nielsen

4

Unity3D

public GameObject asset;

Kemudian Anda lupa untuk menarik & melepas aset di sana dan BOOM, Unity meledak. Terjadi sepanjang waktu.


3
tunggu, pengembangan di unity3d membutuhkan mouse?
Einacio

1
@Einacio jika Anda menginginkan hal-hal yang lebih mudah, Anda cukup menyeret Drop aset ke variabel. Sangat berguna. Tetapi Anda dapat kode tanpa itu jika Anda mau.
Fabricio

4

Rubi

Hanya beberapa kode sederhana untuk mengambil produk array.

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

Dua hal di sini. Salah satunya adalah bahwa ..operator jangkauan inklusif . Jadi 0..xmemiliki x + 1 elemen dan termasuk x. Ini berarti bahwa kita melebihi batas array. Hal lain adalah ketika Anda melakukan ini di Ruby, itu hanya memberi Anda nilkembali. Ini sangat menyenangkan ketika, katakanlah, program Anda melempar exceptsepuluh baris setelah bug.


Ini agak terlalu jelas. Ini seperti melakukannya for (int i = 0; i <= arr.length; i++)di Jawa.
Cole Johnson

3

Android

Saya melihat ini terjadi terlalu sering. Seseorang mengirimkan pesan ke aktivitas berikutnya (mungkin kode status, memetakan data, apa pun), dan akhirnya menarik nol dariIntent .

Sekilas sepertinya cukup masuk akal. Hanya:

  • pastikan pesannya tidak nol
  • masukkan ke dalam niat
  • memulai aktivitas baru
  • dapatkan niat dalam aktivitas baru
  • ekstrak pesan dengan tag

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

Dalam 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 memang mengatakan itu Intent.getStringExtra(String)mungkin kembali null, tetapi hanya jika tag tidak ditemukan. Jelas saya menggunakan tag yang sama, jadi pasti ada yang lain ...


2
Masalah dengan jawaban ini adalah bahwa orang yang tidak terbiasa dengan pengembangan Android (seperti saya) tidak akan bisa menghargainya.
John Dvorak

@ JanDvorak Setuju, tapi bukan masalah besar. Ada jawaban lain di situs yang tidak saya hargai juga. :)
Geobits

3

C

Hanya membaca cepat dari buffer, di mana programmer bahkan cukup baik untuk mendokumentasikan potensi bahaya!

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

Trik yang sangat sederhana dan jelas jika Anda mengharapkan kode berbahaya. Komentar yang mengakhiri '\' lolos dari baris baru, sehingga buffer tidak pernah dialokasikan memori. Kemudian akan gagal scanf, karena buffer akan menjadi NULL (sebagai variabel lingkup file nol diinisialisasi dalam 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)

Apa yang terjadi di sini agak rumit. Di Nimrod, prosedur standar diinisialisasi ke nil. Dalam make_funcpanggilan pertama , itu berhasil. Yang kedua, bagaimanapun, melempar pengecualian dan meninggalkan f2diinisialisasi. Ini kemudian dipanggil, menyebabkan kesalahan.


0

C #

Ini klasik. Metode yang sangat berguna FindStringRepresentationOfakan membuat instance baru dari parameter tipe yang ditentukan, kemudian menemukan representasi string dari instance itu.

Tentunya akan sia-sia untuk memeriksa nullsegera setelah membuat contoh baru, jadi saya belum melakukan itu ...

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

Mengubah objectdeklarasi variabel lokal ke dalam TNewable atau ke var(C # 3 dan yang lebih baru) membuat masalah hilang. Petunjuk: Tinju Nullable<T>(alias T?) tidak biasa di .NET Framework.

Setelah memperbaiki masalah seperti yang dijelaskan dalam teks tak terlihat di atas, coba juga goodInstance.GetType()(perbedaannya adalah GetType(), tidak seperti ToString(), non-virtual, sehingga mereka tidak bisa overridemengetiknya 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;
}

Apa yang Anda harapkan adalah "Hello World!" untuk dicetak. Tetapi Anda hanya akan melihat sampah di layar.

Di sini a1 dihancurkan ketika lingkup init () selesai. Jadi a_ptr, karena menunjuk ke a1, akan menghasilkan sampah.


0

C

#define ONE 0 + 1

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

Penjelasan

Preprosesor tidak benar-benar menghitung 0 + 1, jadi ONEsecara harfiah didefinisikan sebagai 0 + 1, menghasilkan 1 / 0 + 1, yang merupakan pembagian dengan nol, dan menghasilkan pengecualian titik mengambang.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.