Bagaimana cara melempar pengecualian di C?

102

Saya mengetik ini ke google tetapi hanya menemukan howtos di C ++,

bagaimana melakukannya di C?

httpinterpret
sumber
6
C tidak mendukung penanganan pengecualian. Untuk melempar pengecualian di C, Anda perlu menggunakan platform yang spesifik seperti penanganan pengecualian terstruktur Win32 - tetapi untuk memberikan bantuan apa pun dengan itu, kami perlu mengetahui platform yang Anda pedulikan.
Jerry Coffin
18
... dan jangan gunakan penanganan pengecualian terstruktur Win32.
Brian R. Bondy
4
Menggunakan setjmp () dan longjmp () seharusnya, secara teori, berhasil, tapi menurut saya itu tidak sepadan dengan masalahnya.
Joseph Quinsey

Jawaban:

78

Tidak ada pengecualian di C. Dalam C kesalahan diberitahukan oleh nilai yang dikembalikan dari fungsi, nilai keluar dari proses, sinyal ke proses ( Program Error Signals (GNU libc) ) atau gangguan perangkat keras CPU (atau pemberitahuan lainnya kesalahan bentuk CPU jika ada) ( Bagaimana prosesor menangani kasus pembagian dengan nol ).

Pengecualian didefinisikan dalam C ++ dan bahasa lain. Penanganan pengecualian di C ++ ditentukan dalam standar C ++ "Penanganan pengecualian S.15", tidak ada bagian yang setara dalam standar C.

Brian R. Bondy
sumber
4
Jadi di C dijamin tidak akan ada pengecualian, bagaimana caranya?
httpinterpret
62
@httpinterpret: dalam C "dijamin" bahwa tidak ada pengecualian dengan cara yang sama seperti "dijamin" bahwa tidak ada template, atau refleksi, atau unicorn. Spesifikasi bahasa sama sekali tidak mendefinisikan hal seperti itu. Ada setjmp / longjmp, yang bisa digunakan untuk keluar dari fungsi tanpa kembali. Jadi program dapat membangun mekanisme seperti pengecualian mereka sendiri jika mereka mau, atau implementasi C dapat menentukan ekstensi ke standar.
Steve Jessop
2
Sebuah program dengan maindi .cfile yang dapat mencakup beberapa C ++, dan karena itu pengecualian bisa dilemparkan dan tertangkap dalam program ini, tetapi bagian-bagian kode C akan tetap tahu tentang semua ini terjadi kecuali bahwa pengecualian melempar dan menangkap sering mengandalkan fungsi ditulis dalam C yang berada di pustaka C ++. C digunakan karena Anda tidak dapat mengambil risiko fungsi yang dipanggil untuk melakukan throwpengecualian itu sendiri. Mungkin ada cara spesifik compiler / library / target untuk melempar / menangkap pengecualian. Tapi melempar instance kelas akan memiliki masalahnya sendiri.
nategoose
61
@Steve: Tolong beri tahu saya jika Anda menemukan bahasa dengan unicorn, saya telah menunggu hal seperti itu selama bertahun-tahun.
Brian R. Bondy
5
@ BrianR.Bondy Ini adalah bahasa dengan unicorn (saya jamin dengan cara yang sama seperti yang dijamin di C)
Tofandel
38

Di C Anda bisa menggunakan kombinasi dari setjmp()dan longjmp()fungsi, yang didefinisikan di setjmp.h. Contoh dari Wikipedia

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

Catatan: Saya benar-benar menyarankan Anda untuk tidak menggunakannya karena mereka bekerja buruk dengan C ++ (penghancur objek lokal tidak akan dipanggil) dan sangat sulit untuk memahami apa yang sedang terjadi. Kembalikan beberapa jenis kesalahan sebagai gantinya.

vava
sumber
Saya telah melihat setjump / longjump tidak berfungsi dengan benar di program C ++ bahkan ketika tidak ada objek yang perlu dimusnahkan ketika pengecualian digunakan. Saya jarang menggunakannya dalam program C.
nategoose
Ini adalah pendekatan yang menarik menggunakan setjmp. on-time.com/ddj0011.htm Tapi ya, pada dasarnya Anda harus menemukannya sendiri jika Anda ingin eksekusi kode out-of-band tanpa melepas tumpukan.
Dwayne Robinson
Saya tidak yakin mengapa peringatan tentang menggunakannya dalam C ++ ketika pertanyaannya adalah tentang C dan C ++ memiliki pengecualian. Bagaimanapun, penting bagi OP untuk mengetahui bahwa untuk menjaga implementasi setjmp / longjmp dari menembak kaki Anda, selalu ingat bahwa Anda harus terlebih dahulu mengunjungi penangan kesalahan (untuk menyiapkannya), dan penangan kesalahan itu perlu kembali dua kali.
1
Ngomong-ngomong, penanganan pengecualian adalah sesuatu yang benar-benar saya harapkan akan berakhir di C11. Terlepas dari kepercayaan umum, itu tidak memerlukan OOP untuk berfungsi dengan baik, dan C menderita tanpa henti oleh setiap pemanggilan fungsi yang membutuhkan if(). Saya tidak bisa melihat bahaya di dalamnya; bisakah orang lain disini?
@ user4229245 Saya berada di bawah kesan (anekdotal) apa pun yang "dilakukan untuk Anda" dijauhkan dari spesifikasi bahasa c sebanyak mungkin secara khusus untuk menjaga agar c tetap relevan untuk bare metal dan pemrograman mesin di mana bahkan fundamental status quo seperti delapan bit byte isn Tidak universal, hanya pemikiran saya, saya bukan penulis spesifikasi c
ThorSummoner
21

C lama biasa sebenarnya tidak mendukung pengecualian secara native.

Anda dapat menggunakan strategi penanganan kesalahan alternatif, seperti:

  • mengembalikan kode kesalahan
  • kembali FALSEdan menggunakan last_errorvariabel atau fungsi.

Lihat http://en.wikibooks.org/wiki/C_Programming/Error_handling .

Lucas Jones
sumber
Juga api windows memiliki metode yang sama untuk menangani kesalahan. misalnya GetLastError()di windows API.
BattleTested
20

Ada tidak ada built-in mekanisme pengecualian dalam C; Anda perlu menyimulasikan pengecualian dan semantiknya. Ini biasanya dicapai dengan mengandalkan setjmpdan longjmp.

Ada cukup banyak perpustakaan di sekitar, dan saya sedang mengimplementasikan yang lain. Ini disebut exceptionions4c ; portabel dan gratis. Anda dapat melihatnya, dan membandingkannya dengan alternatif lain untuk melihat mana yang paling cocok untuk Anda.

Guillermo Calvo
sumber
Saya membacakan contoh kode, saya memulai proyek serupa dengan struktur, tetapi menggunakan uuid, bukan string untuk mengidentifikasi setiap "pengecualian". Proyek Anda tampaknya sangat menjanjikan.
umlcat
The alternatif Link sekarang harus mengarah ke github.com/guillermocalvo/exceptions4c/wiki/alternatives
Marcel Waldvogel
Apakah Anda (atau orang lain) tahu tentang perbandingan antara paket pengecualian yang berbeda?
Marcel Waldvogel
11

C dapat menampilkan pengecualian C ++, mereka tetap kode mesin. Misalnya, di bar.c

// 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

di a.cc,

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

untuk menyusunnya

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

keluaran

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html memiliki info lebih detail tentang __cxa_throw.

Saya tidak yakin apakah ini portabel atau tidak, dan saya mengujinya dengan 'gcc-4.8.2' di Linux.

wcy
sumber
2
Apa itu "_ZTIl" ??? Saya tidak dapat menemukan referensi untuk itu di mana pun dan itu adalah misteri lengkap bagaimana memungkinkan kode C memiliki akses ke std :: type_info. Tolong bantu aku!
AreusAstarte
1
_ZTIIartinya typeinfo for long, mis echo '_ZTIl' | c++filt . Ini adalah skema mangling g ++.
wcy
dan hanya untuk ukuran yang baik, pastikan untuk memanggil simbol ac di blok tangkapan untuk menghindari c ++ sebanyak mungkin: P (PS terima kasih telah membagikan contoh nyata yang sangat baik!)
ThorSummoner
8

Pertanyaan ini sangat tua, tetapi saya baru saja menemukan itu dan berpikir saya akan berbagi teknik: bagi dengan nol, atau dereferensi penunjuk nol.

Pertanyaannya hanyalah "bagaimana cara melempar", bukan bagaimana cara menangkap, atau bahkan bagaimana cara melempar jenis pengecualian tertentu. Saya memiliki situasi berabad-abad yang lalu di mana kami perlu memicu pengecualian dari C untuk ditangkap di C ++. Secara khusus, kami memiliki laporan sesekali tentang kesalahan "panggilan fungsi virtual murni", dan perlu meyakinkan fungsi _purecall runtime C untuk melempar sesuatu. Jadi kami menambahkan fungsi _purecall kami sendiri yang dibagi dengan nol, dan boom, kami mendapat pengecualian yang dapat kami tangkap di C ++, dan bahkan menggunakan beberapa stack fun untuk melihat di mana ada yang salah.

Joe
sumber
@AreusAstarte untuk berjaga-jaga jika seseorang benar-benar perlu diperlihatkan pembagian C oleh nol:int div_by_nil(int x) { return x/0; }
7

Tentang Win with MSVC ada __try ... __except ...tetapi itu benar-benar mengerikan dan Anda tidak ingin menggunakannya jika Anda mungkin dapat menghindarinya. Lebih baik mengatakan bahwa tidak ada pengecualian.

Donal Fellows
sumber
1
Sebenarnya pengecualian C ++ dibangun di atas SEH juga di bawah kap.
Calmarius
@ Calmarius Apa SEH ?
Marcel Waldvogel
1
@MarcelWaldvogel Structured Exception Handling (cara Windows menangani pengecualian CPU).
Calmarius
6

C tidak memiliki pengecualian.

Ada berbagai implementasi hacky yang mencoba melakukannya (salah satu contohnya di: http://adomas.org/excc/ ).

Joe
sumber
3

Seperti disebutkan di banyak utas, cara "standar" untuk melakukan ini adalah menggunakan setjmp / longjmp. Saya memposting solusi lain seperti itu ke https://github.com/psevon/exceptions-and-raii-in-c. Sepengetahuan saya, ini adalah satu-satunya solusi yang mengandalkan pembersihan otomatis sumber daya yang dialokasikan. Ini mengimplementasikan smartpointers yang unik dan bersama, dan memungkinkan fungsi perantara untuk membiarkan pengecualian lewat tanpa menangkap dan masih memiliki sumber daya yang dialokasikan secara lokal dibersihkan dengan benar.

pengguna3510229
sumber
2

C tidak mendukung pengecualian. Anda dapat mencoba mengompilasi kode C Anda sebagai C ++ dengan Visual Studio atau G ++ dan melihat apakah itu akan dikompilasi sebagaimana adanya. Sebagian besar aplikasi C akan dikompilasi sebagai C ++ tanpa perubahan besar, dan Anda kemudian dapat menggunakan sintaks try ... catch.

Mahmoud Al-Qudsi
sumber
0

Jika Anda menulis kode dengan pola desain Happy path (yaitu untuk perangkat tertanam) Anda dapat mensimulasikan pemrosesan kesalahan pengecualian (alias menunda atau akhirnya emulasi) dengan operator "goto".

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

Ini tidak benar-benar pengecualian penangan tetapi itu membawa Anda cara untuk menangani kesalahan saat keluar dari fucntion.

PS Anda harus berhati-hati menggunakan goto hanya dari cakupan yang sama atau lebih dalam dan tidak pernah melompati deklarasi variabel.

Vitold S.
sumber