C'de bir istisna nasıl atılır?


106

Bunu google'a yazdım ama sadece C ++ 'da howtos buldum,

C'de nasıl yapılır?


7
C, istisna işlemeyi desteklemez. C'de bir istisna atmak için, Win32'nin yapılandırılmış istisna yönetimi gibi platforma özgü bir şey kullanmanız gerekir - ancak bununla ilgili herhangi bir yardım sağlamak için, ilgilendiğiniz platformu bilmemiz gerekir.
Jerry Coffin

18
... ve Win32 yapılandırılmış özel durum işlemeyi kullanmayın.
Brian R. Bondy

4
Teorik olarak setjmp () ve longjmp () kullanmak işe yarar, ancak zahmete değeceğini düşünmüyorum.
Joseph Quinsey

Yanıtlar:


79

C'de istisna yoktur. C'de hatalar, işlevin döndürülen değeri, işlemin çıkış değeri, sürece gönderilen sinyaller ( Program Hata Sinyalleri (GNU libc) ) veya CPU donanım kesintisi (veya diğer bildirimler) ile bildirilir. varsa CPU hatası) ( İşlemci sıfıra bölme durumunu nasıl ele alır ).

İstisnalar C ++ ve diğer dillerde tanımlanmıştır. C ++ 'da özel durum işleme, C ++ standardı "S.15 İstisna işleme" içinde belirtilmiştir, C standardında eşdeğer bir bölüm yoktur.


4
Yani C'de istisnaların olmayacağı garantidir, nasıl?
http yorum

65
@httpinterpret: C'de hiçbir istisna olmadığı "garantilidir", tıpkı şablon olmaması, yansıma olmaması veya tek boynuzlu at olmaması "garantilidir". Dil spesifikasyonu böyle bir şeyi tanımlamaz. Bir işlevden geri dönmeden çıkmak için kullanılabilen setjmp / longjmp vardır. Böylece programlar, isterlerse kendi istisna benzeri mekanizmalarını oluşturabilir veya bir C uygulaması standart için uzantıları tanımlayabilir.
Steve Jessop

2
Bir dosyada mainbulunan bir program .c, bazı C ++ içerebilir ve bu nedenle istisnalar programa atılabilir ve yakalanabilir, ancak C kodu bölümleri, istisna atma ve yakalama genellikle C ile yazılmış işlevlere bağlı olması dışında, tüm bunlardan habersiz kalacaktır. C ++ kitaplıklarında bulunur. C kullanılır, çünkü çağrılan işlevin throwkendisine bir istisna atma ihtiyacı duyma riskini alamazsınız . Yine de, istisnaları atmanın / yakalamanın özel bir yolu olabilir. Ancak bir sınıf örneği atmanın kendi sorunları olacaktır.
nategoose

61
@Steve: Eğer tek boynuzlu atlarla bir dil bulursanız lütfen bana bildirin, yıllardır böyle bir şeyi bekliyorum.
Brian R. Bondy

5
@ BrianR.Bondy İşte tek boynuzlu bir dil (C'de garanti edildiği gibi garanti
ediyorum

38

C'de tanımlanan setjmp()ve longjmp()işlevlerinin kombinasyonunu kullanabilirsiniz setjmp.h. Wikipedia'dan örnek

#include <stdio.h>
#include <setjmp.h>

static jmp_buf buf;

void second(void) {
    printf("second\n");         // prints
    longjmp(buf,1);             // jumps back to where setjmp 
                                //   was called - making setjmp now return 1
}

void first(void) {
    second();
    printf("first\n");          // does not print
}

int main() {   
    if ( ! setjmp(buf) ) {
        first();                // when executed, setjmp returns 0
    } else {                    // when longjmp jumps back, setjmp returns 1
        printf("main");         // prints
    }

    return 0;
}

Not: Aslında tavsiye ederiz değil onlar C korkunç çalışırken bunları kullanmak için ++ (yerel nesnelerin yıkıcılar denilen olsun olmazdı) ve ne olup bittiğini anlamak gerçekten zor. Bunun yerine bir tür hata döndür.


İstisnalar kullanıldığında imha edilmesi gereken hiçbir nesne olmasa bile setjump / longjump'ın C ++ programlarında düzgün çalışmadığını gördüm. Bunları nadiren C programlarında kullanırım.
nategoose

Bu setjmp kullanan ilginç bir yaklaşımdı. on-time.com/ddj0011.htm Ama evet, temelde yığını çözmeden bant dışı kod yürütme istiyorsanız bunları kendiniz icat etmelisiniz.
Dwayne Robinson

Soru C ve C ++ ile ilgili olduğunda bunları C ++ 'da kullanma konusundaki ihtarın yine de istisnaları olduğundan emin değilim. Her durumda, OP'nin, setjmp / longjmp uygulamasının ayağınızı çekmesini önlemek için, her zaman önce hata işleyiciyi (onu kurmak için) ziyaret etmeniz gerektiğini ve bu hata işleyiciyi ziyaret etmeniz gerektiğini unutmayın. iki kez dönmesi gerekiyor.

1
Bu arada, istisna işleme, C11'de sona ereceğini umduğum bir şeydi. Yaygın inanışa rağmen, OOP'nin düzgün çalışması için gerekli değildir ve C, bir if(). İçinde bir tehlike göremiyorum; burada başka biri olabilir mi?

@ user4229245 "Sizin için yapılan" herhangi bir şey (anekdot) izlenimi altındayım, özellikle c'yi, sekiz bit bayt gibi statüko temellerinin bile olmadığı çıplak metal ve makine programlamasıyla alakalı tutmak için mümkün olduğunca c dili spesifikasyonunun dışında tutulur evrensel değil, sadece düşüncelerim, özel bir yazar değilim
ThorSummoner

21

Düz eski C aslında istisnaları yerel olarak desteklemez.

Aşağıdakiler gibi alternatif hata işleme stratejileri kullanabilirsiniz:

  • bir hata kodu döndürmek
  • FALSEbir last_errordeğişken veya işlevi döndürme ve kullanma .

Bkz. Http://en.wikibooks.org/wiki/C_Programming/Error_handling .


Ayrıca windows api, hataları işlemek için aynı yönteme sahiptir. örneğin GetLastError()Windows API'de.
BattleTested

20

C'de yerleşik bir istisna mekanizması yoktur ; istisnaları ve anlamlarını simüle etmeniz gerekir . Bu genellikle setjmpve güvenerek elde edilir longjmp.

Etrafta epeyce kütüphane var ve bir tane daha uyguluyorum. Buna istisnalar4c denir ; taşınabilir ve ücretsizdir. Hangisinin size en uygun olduğunu görmek için ona bir göz atabilir ve diğer alternatiflerle karşılaştırabilirsiniz .


Size kod örneği okudum, benzer bir projeye bir yapı ile başladım, ancak her "istisnayı" tanımlamak için bir dize yerine bir uuid kullanıyor. Projeniz çok umut verici görünüyor.
umlcat

Alternatifleri bağlantı artık işaret etmelidir github.com/guillermocalvo/exceptions4c/wiki/alternatives
Marcel Waldvogel

Siz (veya başka biri) farklı istisna paketleri arasındaki bir karşılaştırmayı biliyor musunuz?
Marcel Waldvogel

11

C, C ++ istisnası atabilir, bunlar zaten makine kodlarıdır. Örneğin, bar.c içinde

// begin bar.c
#include <stdlib.h>
#include <stdint.h>
extern void *__cxa_allocate_exception(size_t thrown_size);
extern void __cxa_throw (void *thrown_exception, void* *tinfo, void (*dest) (void *) );
extern void * _ZTIl; // typeinfo of long
int bar1()
{
   int64_t * p = (int64_t*)__cxa_allocate_exception(8);
   *p = 1976;
   __cxa_throw(p,&_ZTIl,0);
  return 10;
}
// end bar.c

a.cc içinde

#include <stdint.h>
#include <cstdio>
extern "C" int bar1();
void foo()
{
  try{
    bar1();
  }catch(int64_t x){
    printf("good %ld",x);
  }
}
int main(int argc, char *argv[])
{
  foo();
  return 0;
}

derlemek için

gcc -o bar.o -c bar.c && g++ a.cc bar.o && ./a.out

çıktı

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html hakkında daha ayrıntılı bilgi var __cxa_throw.

Taşınabilir olup olmadığından emin değilim ve Linux'ta 'gcc-4.8.2' ile test ediyorum.


2
"_ZTIl" ne demek ??? Hiçbir yerde herhangi bir referans bulamıyorum ve C kodunun std :: type_info'ya erişimini nasıl mümkün kıldığı tam bir muamma. Lütfen bana yardım et!
AreusAstarte

1
_ZTIIanlamına gelir typeinfo for long, örn echo '_ZTIl' | c++filt . Bu, g ++ değiştirme şemasıdır.
wcy

ve sadece iyi bir önlem için, c ++ 'dan olabildiğince kaçınmak için catch bloğunda ac sembolünü çağırdığınızdan emin olun: P (PS, mükemmel bir gerçek örnek paylaştığınız için teşekkür ederiz!)
ThorSummoner

8

Bu soru çok eski, ama ben sadece ona rastladım ve bir tekniği paylaşacağımı düşündüm: sıfıra bölmek veya boş göstericiyi referans almak.

Soru basitçe "nasıl fırlatılır", nasıl yakalanacağı, hatta belirli bir istisna türünün nasıl atılacağı değil. C ++ 'da yakalanmak için C'den bir istisnayı tetiklememiz gereken yıllar önce bir durum yaşadım. Özellikle, "saf sanal işlev çağrısı" hatalarıyla ilgili ara sıra raporlar aldık ve C çalışma zamanının _purecall işlevini bir şeyler atmaya ikna etmemiz gerekiyordu. Böylece sıfıra bölünen kendi _purecall işlevimizi ekledik ve patlama, C ++ 'da yakalayabileceğimiz bir istisna var ve hatta işlerin nerede ters gittiğini görmek için biraz yığın eğlencesi kullanıyoruz.


@AreusAstarte, birisinin gerçekten sıfıra C bölümü gösterilmesi gerektiğinde:int div_by_nil(int x) { return x/0; }

7

MSVC ile Win'de var __try ... __except ...ama bu gerçekten korkunç ve eğer bundan kaçınabiliyorsanız kullanmak istemezsiniz. İstisnaların olmadığını söylemek daha iyi.


1
Aslında C ++ istisnaları, SEH'nin tepesinde de kaputun altında inşa edilmiştir.
Calmarius

@Calmarius SEH nedir?
Marcel Waldvogel

1
@MarcelWaldvogel Yapılandırılmış Özel Durum İşleme (Windows'un CPU istisnalarını ele alma yöntemi).
Calmarius


3

Birçok iş parçacığında bahsedildiği gibi, bunu yapmanın "standart" yolu setjmp / longjmp kullanmaktır. Https://github.com/psevon/exceptions-and-raii-in-c adresinde böyle bir başka çözüm daha yayınladım. Bu, tahsis edilen kaynakların otomatik olarak temizlenmesine dayanan bildiğim tek çözümdür. Eşsiz ve paylaşılan akıllı işaretçiler uygular ve ara işlevlerin istisnaların yakalanmadan geçmesine ve yerel olarak tahsis edilen kaynaklarının düzgün bir şekilde temizlenmesine izin vermesine izin verir.


2

C istisnaları desteklemez. C kodunuzu Visual Studio veya G ++ ile C ++ olarak derlemeyi deneyebilir ve olduğu gibi derlenip derlenmeyeceğini görebilirsiniz. Çoğu C uygulaması, büyük değişiklikler olmadan C ++ olarak derlenir ve ardından try ... catch sözdizimini kullanabilirsiniz.


0

Mutlu yol tasarım deseniyle (yani gömülü aygıt için) kod yazarsanız, "goto" operatörüyle istisna hatası işlemeyi (diğer bir deyişle, deffering veya son olarak emülasyon) simüle edebilirsiniz.

int process(int port)
{
    int rc;
    int fd1;
    int fd2;

    fd1 = open("/dev/...", ...);
    if (fd1 == -1) {
      rc = -1;
      goto out;
    }

    fd2 = open("/dev/...", ...);
    if (fd2 == -1) {
      rc = -1;
      goto out;
    }

    // Do some with fd1 and fd2 for example write(f2, read(fd1))

    rc = 0;

   out:

    //if (rc != 0) {
        (void)close(fd1);
        (void)close(fd2);
    //}

    return rc;
}

Aslında istisna işleyici değildir, ancak fucntion çıkışındaki hatayı ele almanın bir yolunu alır.

Not: Dikkatli olmalısınız, sadece aynı veya daha fazla derin kapsamdan gidin ve değişken bildirimini asla atlamayın.


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.