Boş gösterici istisnası atın [kapalı]


14

Göreviniz bir boş gösterici istisnası oluşturmaktır. Diğer bir deyişle, programınız boş değerli olmasını beklediği bir değeri kabul etmeli ve değer boş olduğu için bir istisna / hata veya kilitlenme atmalıdır.

Ayrıca, kodun okunmasından değerin boş olduğu açık değildir. Amacınız, okuyucuya değerin, gerçekte olsa bile, null olmadığını açıkça göstermesini sağlamaktır.

  • Null yerine nil, none, hiçbir şey ya da eşdeğeri dilinizde ne olursa olsun kullanabilirsiniz. Ayrıca tanımsız, başlatılmamış vb. Kullanabilirsiniz.
  • Kodunuzdaki sorun, değişkenin (şaşırtıcı bir şekilde) programın boş olmayan bir değişken beklediği yerde null olması olmalıdır.
  • Programınız bir istisna atarak, bir hata atarak, çökerek veya beklenmeyen bir null ile karşılaşıldığında normalde ne yaparsa null'a yanıt verebilir.

Bu bir popülerlik yarışması, bu yüzden akıllı ol!


@Ourous Ne demek istediğinizi göstermek için bir örnek verebilir misiniz?
Ypnypn

Üzerine baktıktan sonra, aradığınızdan çok bir döküm hatası.
14'te

Derleyici hatası kullanma iznim var mı?
Mark

1
@ Mark Bu bir popülerlik yarışmasıdır; topluluk karar versin. Derleyici bir hataya kesinlikle oy verirdim.
11684

Yanıtlar:


33

Java

Bir sayının mutlak değerini hesaplayalım. Java'nın bu amaçla Math.abs vardır, ancak kullandığımız sayı bazen boş olabilir. Dolayısıyla bu durumla başa çıkmak için bir yardımcı yönteme ihtiyacımız var:

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 null ise, null değerini döndürün, Math.abs () kullanın.
Kod çok basit ve açıktır ve iyi çalışmalıdır ... değil mi?

Bu arada, bu satırı kullanmak yerine:

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

düzgün çalışıyor. Bunu bir spoiler yapmayı düşündüm, ama .. sanırım bu kadar şaşırtıcı :)

Tamam, bir açıklama:

İlk olarak, Math.abs bir Tamsayı almaz, bir int (diğer sayısal türler için aşırı yüklenmiş yöntemler de vardır) ve ayrıca bir int döndürür. Java'da int, ilkel bir türdür (ve null olamaz) ve Integer karşılık gelen sınıftır (ve null olabilir). Java sürüm 5'ten beri, int ve Integer arasında bir dönüştürme gerektiğinde otomatik olarak gerçekleştirilir. Böylece Math.abs, x alabilir, otomatik olarak int'e dönüştürülebilir ve bir int döndürür.

Şimdi garip kısım: üçlü operatör (? :) biri ilkel tipte diğeri karşılık gelen sınıfa (int ve Integer gibi) sahip 2 ifadeyle uğraşmak zorunda kaldığında, java'nın ilkel dönüşümü beklenir sınıfa (diğer adıyla "boks"), özellikle de ihtiyaç duyduğu tip (burada yöntemden dönmek için) referans (sınıf) tipidir ve ilk ifade de referans tipindedir. Ancak java tam tersini yapar: referans türünü ilkel türe (diğer bir deyişle "kutudan çıkarma") dönüştürür. Yani bizim durumumuzda, x'i int'e dönüştürmeye çalışır, ancak int boş olamaz, bu nedenle bir NPE atar.

Bu kodu Eclipse kullanarak derlerseniz, aslında bir uyarı alırsınız: "Boş işaretçi erişimi: Integer türündeki bu ifade null, ancak otomatik kutu açma gerektirir"


/programming/7811608/java-npe-in-ternary-operator-with-autoboxing
/programming/12763983/nullpointerexception-through-auto-boxing-behavior-of-java -üçlü operatör



SPOILERS x! = Null olduğunda ne olur? (Bunu çoktan anladım.)
11684

@ 11684 x ​​boş olmadığında gayet iyi çalışıyor
SEits EVIL

Sonra bunun nasıl çalıştığını bilmediğimi düşünüyorum. Eğer x null olmadığında çalışıyorsa ve bunun neden atıldığı konusunda haklıyım, kolon iki anlamda ayrıştırılmalıdır (ki bu bir Dil Spesifikasyonuna koymam).
11684

@ 11684 "iki duyu" hakkında ne demek istediğinden emin değilim, ama yine de şimdi bir açıklama ekledim
SEitsu SE çünkü SEVDİ

23

C

Her C programcısı en az bir kez bu hatayı yaptı.

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

Sebep:

scanfint *argümanda bir pointer ( ) alır , işte burada 0(NULL-pointer)

Düzeltme:

scanf("%d",&n);

http://ideone.com/MbQhMM


1
Tam olarak öyle değil. scanfdeğişken bir işlevdir ve intbeklendiği zaman yanlış tip ( ) argümanı verilmiştir int *. C (Biliyorum, derleyiciler bunu tespit edebilir scanf) varyasyon fonksiyonundaki türleri kontrol edemez, bu mutlu bir şekilde derler. 0gerçekten boş gösterici değişmezidir, ancak bu yalnızca doğrudan kullanılan değişmezin kendisi için bir değişken içinde saklanmadan geçerlidir. nasla boş gösterici içermedi.
Konrad Borowski

Bu işlevi düzeltmek için scanf'in dönüş değerini de kontrol etmeniz gerekir.
David Grayson

1
@David Dönüş değerini kontrol etmeden doğru değil, ancak çökmeyecek veya UB'yi çağırmayacak - n 0'da kalacak. (Bunun olduğunu görmek için boş bir dosyayı stdin olarak yönlendirmeyi deneyin.)
Riking

14

PHP

Bu beni birkaç kez ısırdı.

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

Beklenen Sonuç:

Hello, World!

Gerçek sonuç:

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

aka NullPointerException

Sebep:

Varsayılan olarak, yapıcı sözdizimi PHP 4.x ile geriye doğru uyumludur ve bu nedenle, işlev foosınıf için geçerli bir yapıcıdır Foove bu nedenle varsayılan boş yapıcıyı geçersiz kılar. Bu tür bir hata, projenize bir ad alanı eklenerek önlenebilir.


9

CoffeeScript (Node.js üzerinde)

CoffeeScript'te ?varoluşçu işleçtir . Değişken varsa, kullanılır, aksi takdirde sağ taraf kullanılır. Taşınabilir programlar yazmanın zor olduğunu hepimiz biliyoruz. Bu durumda, JavaScript'te yazdırma aşağıda belirtilmiştir. Tarayıcılar alert(veya document.write), SpiderMonkey kabuğu kullanır printve Node.js kullanır console.log. Bu çılgınca, ancak CoffeeScript bu soruna yardımcı oluyor.

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

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

Bunu Node.js altında çalıştıralım. Sonuçta, senaryomuzun çalıştığından emin olmak istiyoruz.

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, neden alerttanımlanmadığı zaman neden şikayet edesin ki ?

Nedense, CoffeeScript'te ilişkisel ?bırakılır, yani tanımsız değişkenleri sadece sol taraf için yok sayar. Düzeltilmemiş, çünkü görünüşe göre bazı geliştiriciler güvenebilir mi? çağrışımsal olmak .


8

Yakut

Bir Unix klonunun PATH'sinde awk'yi bulun.

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

Hata!

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

Hata, biz p.finddenir nil.find, öyle polması gerektiğini biliyoruz nil. Bu nasıl oldu?

Ruby'de defyerel değişkenler için kendi kapsamı vardır ve yerel değişkenleri hiçbir zaman dış kapsamdan almaz. Dolayısıyla ödev p = ENV['PATH'].split ':'kapsam dahilinde değildir.

Tanımlanmamış bir değişken genellikle neden olur NameError, ancak pözel bir durumdur. Ruby adında küresel bir yönteme sahiptir p. Yani p.find { ... }bir yöntem çağrısı olur, gibi p().find { ... }. Bağımsız pdeğişken olmadığında geri döner nil. (Kod golfçüler pkısayol olarak kullanır nil.) Sonra nil.find { ... }yükselir NoMethodError.

Programı Python'da yeniden yazarak düzeltirim.

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

İşe yarıyor!

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

Muhtemelen yazdırmasını istiyorum awk is /usr/bin/awk, ama bu farklı bir hata.


7

C #

With()stringaslında sadece bir takma ad olan bir nesne genişletme yöntemidir 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;
        }
    }
}

İyi görünüyor, değil mi? Yanlış.

Type.GetType()tam nitelikli, büyük / küçük harfe duyarlı bir tür adı gerektirir. Sorun şu ki System.stringmevcut değil; stringsadece bir takma addır gerçek türü: System.String. Çalışması gerektiği gibi görünüyor, ancak str.GetMethod()istisnayı atacak çünkü str == null.

Dilin iç özelliklerini biraz bilen çoğu insan muhtemelen sorunu oldukça hızlı bir şekilde tespit edebilecek, ancak yine de bir bakışta kolayca gözden kaçan bir şey.


Bu harika bir şey: D
Knerd

Bu biraz fazla açık. Orada aynı şeyi yapmak için iki yol vardır çünkü okuyucu hemen bir ipucu almak .GetType()ve typeof()sonuç aynı değilken ve böylece arızaya neden olur. Ben poster değil kurşun geçirmez görünümlü kodu istedi düşünüyorum.
ja72

Yansıma bu genişletme yönteminde neden kullanılacak? Açık olan kod return string.Format(format, args);. Yansıtma gerekiyorsa (başka bir kullanım durumunda), typeof(string)statik GetTypeyöntem değil, (derleme zamanında kasa hatalarını yakalar, sihirli dize yok) kullanırdı . Yani örnek "gerçekçi değil" gibi görünüyor.
Jeppe Stig Nielsen

4

Unity3D

public GameObject asset;

Sonra varlığı sürükleyip bırakmayı unutursun ve BOOM, Unity patlar. Her zaman olur.


3
bekleyin, unity3d geliştirme fare gerektirir?
Einacio

1
@Einacio Eğer işleri daha kolay istiyorsanız sadece bir varlığı bir değişkene sürükleyip bırakabilirsiniz. Çok kullanışlı. Ama isterseniz bu olmadan kod yazabilirsiniz.
Fabricio

4

Yakut

Sadece bir dizi ürün almak için basit bir kod.

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

Burada iki şey var. Birincisi, ..aralık operatörünün kapsayıcı olmasıdır . Yani 0..xsahiptir x + 1 elemanları ve x içerir. Bu, dizinin sınırlarını aştığımız anlamına gelir. Başka bir şey, Ruby'de bunu yaptığınızda, size sadece bir nilgeri verir . Diyelim ki, programınız hatadan sonraexcept on satır attığında bu çok eğlencelidir .


Bu çok açık. for (int i = 0; i <= arr.length; i++)Java ile yapmak gibi .
Cole Johnson

3

Android

Bunun çok sık olduğunu görüyorum. Bir kişi bir sonraki aktiviteye bir mesaj iletir (belki bir durum kodu, harita verisi, her neyse) ve Intent.

İlk bakışta oldukça makul görünüyor. Sadece:

  • mesajın boş olmadığından emin ol
  • niyetine topla
  • yeni etkinlik başlat
  • yeni etkinliklere niyetlenmek
  • etikete göre mesaj çıkar

İçinde 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);
}

İçinde 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 yapar söyleyebiliriz Intent.getStringExtra(String)döndürebilir nullama sadece etiket bulunamadı eğer. Açıkçası aynı etiketi kullanıyorum, bu yüzden başka bir şey olmalı ...


2
Bu cevabın problemi, Android geliştirmesine (benim gibi) aşina olmayan kişilerin bunu takdir edemeyeceğidir.
John Dvorak

@ JanDvorak Kabul etti, ama önemli değil. Sitede tam olarak takdir etmediğim başka cevaplar da var. :)
Geobits

3

C

Programlayıcının potansiyel tehlikeyi belgeleyecek kadar nazik olduğu tampondan sadece hızlı bir şekilde okuyun!

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

Kötü amaçlı kod bekliyorsanız çok basit ve bariz bir hile. '\' İle biten yorum satır sonundan kaçar, böylece ara belleğe hiçbir zaman bellek ayrılmaz. Ardından tampon NULL olacağından scanf başarısız olur (dosya kapsamı değişkenleri C'de sıfır başlatıldığından).


0

Nemrut

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)

Burada olanlar biraz karmaşık. Nemrut'ta, prosedürler varsayılan olarak başlatılır nil. İlk make_funcçağrıda başarılı olur. Ancak ikincisi bir istisna atar ve f2başlatılmamış bırakır . Daha sonra bir hataya neden olarak çağrılır.


0

C #

Bu klasik. Çok kullanışlı bir yöntem FindStringRepresentationOf, daha sonra belirtilen tür parametresi yeni bir örneğini oluşturmak o örneğe dize temsilini bulacaksınız.

Elbette nullyeni bir örnek oluşturduktan hemen sonra kontrol etmek israf olacaktır , bu yüzden bunu yapmadım ...

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

objectYerel değişken bildiriminde (C # 3 ve üstü) TNewable veya içine değiştirmek varsorunu ortadan kaldırır. İpucu:Nullable<T> (aka T?) 'nın boksu .NET Framework'te anormaldir.

Videoları denemede, yukarıda görünmez metinde anlatıldığı gibi sorunu sabit ettikten sonra goodInstance.GetType()(özellikleri farklı olan GetType()farklı olarak ToString(), sanal olmayan yani başaramadılar, overridetip o 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;
}

Beklediğiniz şey "Merhaba Dünya!" basılacak. Ama ekranda sadece çöp göreceksiniz.

Burada a1, init () kapsamı tamamlandığında yok edilir. Yani a_ptr, a1'e işaret ettiği gibi çöp üretecektir.


0

C

#define ONE 0 + 1

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

açıklama

Önişlemci gerçekte hesaplamaz 0 + 1, bu yüzden ONEtam anlamıyla 0 + 1, 1 / 0 + 1sıfıra bölünen ve kayan nokta istisnasına neden olan olarak tanımlanır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.