Sejak saya menyadari beberapa tahun yang lalu, bahwa ini tidak menghasilkan kesalahan secara default (setidaknya di GCC), saya selalu bertanya-tanya mengapa?
Saya mengerti bahwa Anda dapat mengeluarkan flag compiler untuk menghasilkan peringatan, tetapi bukankah itu selalu menjadi kesalahan? Mengapa masuk akal jika fungsi non-void tidak mengembalikan nilai yang valid?
Contoh seperti yang diminta dalam komentar:
#include <stdio.h>
int stringSize()
{
}
int main()
{
char cstring[5];
printf( "the last char is: %c\n", cstring[stringSize()-1] );
return 0;
}
... mengkompilasi.
-Werror=return-type
akan memperlakukan peringatan itu hanya sebagai kesalahan. Saya hanya mengabaikan peringatan dan beberapa menit frustrasi melacakthis
pointer yang tidak valid membawa saya ke sini dan sampai pada kesimpulan ini.std::optional
fungsi tanpa mengembalikan mengembalikan opsional "benar"Jawaban:
Standar C99 dan C ++ tidak memerlukan fungsi untuk mengembalikan nilai. Pernyataan pengembalian yang hilang dalam fungsi pengembalian nilai akan didefinisikan (untuk kembali
0
) hanya dalammain
fungsi.Alasannya mencakup memeriksa apakah setiap jalur kode mengembalikan nilai yang cukup sulit, dan nilai balik dapat diatur dengan assembler tertanam atau metode rumit lainnya.
Dari konsep C ++ 11 :
§ 6.6.3 / 2
§ 3.6.1 / 5
Perhatikan bahwa perilaku yang dijelaskan dalam C ++ 6.6.3 / 2 tidak sama dengan C.
gcc akan memberi Anda peringatan jika Anda menyebutnya dengan opsi -Wreturn-type.
Sama seperti rasa ingin tahu, lihat apa yang dilakukan kode ini:
Kode ini secara formal memiliki perilaku yang tidak terdefinisi, dan dalam praktiknya itu memanggil konvensi dan arsitektur bergantung. Pada satu sistem tertentu, dengan satu kompiler tertentu, nilai kembali adalah hasil dari evaluasi ekspresi terakhir, disimpan dalam
eax
register prosesor sistem itu.sumber
throw
ataulongjmp
, haruskah kompiler memerlukan panggilan yang tidak terjangkaureturn
setelah panggilan ke fungsi yang tidak kembali? Kasus di mana itu tidak diperlukan tidak terlalu umum, dan persyaratan untuk memasukkannya bahkan dalam kasus seperti itu mungkin tidak akan berat, tetapi keputusan untuk tidak mengharuskannya masuk akal.gcc tidak secara default memeriksa bahwa semua jalur kode mengembalikan nilai karena secara umum ini tidak dapat dilakukan. Ini mengasumsikan Anda tahu apa yang Anda lakukan. Pertimbangkan contoh umum menggunakan enumerasi:
Anda pemrogram tahu bahwa, kecuali bug, metode ini selalu mengembalikan warna. gcc percaya bahwa Anda tahu apa yang Anda lakukan sehingga tidak memaksa Anda untuk mengembalikan fungsi.
javac, di sisi lain, mencoba memverifikasi bahwa semua jalur kode mengembalikan nilai dan melemparkan kesalahan jika tidak dapat membuktikan bahwa mereka semua melakukannya. Kesalahan ini diamanatkan oleh spesifikasi bahasa Java. Perhatikan bahwa kadang-kadang itu salah dan Anda harus memasukkan pernyataan pengembalian yang tidak perlu.
Ini perbedaan filosofis. C dan C ++ lebih permisif dan lebih bisa dipercaya daripada bahasa Java atau C # sehingga beberapa kesalahan dalam bahasa yang lebih baru adalah peringatan di C / C ++ dan beberapa peringatan diabaikan atau dinonaktifkan secara default.
sumber
System.exit()
tidak pernah kembali.System.exit()
tidak pernah kembali. Saya mencarinya ( java.sun.com/j2se/1.4.2/docs/api/java/lang/… ), dan dokumen hanya mengatakan itu "tidak pernah biasanya kembali". Saya ingin tahu apa artinya itu ... #Maksud Anda, mengapa mengalir dari akhir fungsi pengembalian nilai (yaitu keluar tanpa eksplisit
return
) bukan kesalahan?Pertama, di C apakah suatu fungsi mengembalikan sesuatu yang bermakna atau tidak hanya penting ketika kode yang mengeksekusi benar-benar menggunakan nilai yang dikembalikan. Mungkin bahasa tidak ingin memaksa Anda untuk mengembalikan apa pun ketika Anda tahu bahwa Anda tidak akan menggunakannya lagi sebagian besar waktu.
Kedua, ternyata spesifikasi bahasa tidak ingin memaksa penulis kompiler untuk mendeteksi dan memverifikasi semua jalur kontrol yang mungkin untuk keberadaan eksplisit
return
(walaupun dalam banyak kasus ini tidak terlalu sulit untuk dilakukan). Juga, beberapa jalur kontrol mungkin mengarah ke fungsi yang tidak kembali - sifat yang umumnya tidak diketahui oleh kompiler. Jalan seperti itu bisa menjadi sumber positif palsu yang menjengkelkan.Perhatikan juga, bahwa C dan C ++ berbeda dalam definisi perilaku dalam kasus ini. Dalam C + + hanya mengalir dari akhir fungsi nilai yang kembali selalu perilaku tidak terdefinisi (terlepas dari apakah hasil fungsi digunakan oleh kode panggilan). Dalam C ini menyebabkan perilaku yang tidak terdefinisi hanya jika kode panggilan mencoba menggunakan nilai yang dikembalikan.
sumber
return
pernyataan dari akhirmain()
?main
spesial dalam hal itu.Adalah legal di bawah C / C ++ untuk tidak kembali dari fungsi yang mengklaim mengembalikan sesuatu. Ada sejumlah kasus penggunaan, seperti panggilan
exit(-1)
, atau fungsi yang memanggilnya atau melempar pengecualian.Kompiler tidak akan menolak C ++ legal meskipun itu mengarah ke UB jika Anda memintanya. Secara khusus, Anda meminta tidak ada peringatan yang dihasilkan. (Gcc masih mengaktifkan beberapa secara default, tetapi ketika ditambahkan mereka tampaknya sejajar dengan fitur baru, bukan peringatan baru untuk fitur lama)
Mengubah no-arg gcc default untuk memancarkan beberapa peringatan bisa menjadi perubahan besar untuk skrip yang ada atau membuat sistem. Yang dirancang dengan baik baik
-Wall
dan menangani peringatan, atau beralih peringatan individu.Belajar menggunakan rantai alat C ++ adalah penghalang untuk belajar menjadi programmer C ++, tetapi rantai alat C ++ biasanya ditulis oleh dan untuk para ahli.
sumber
Makefile
saya harus berjalan dengan-Wall -Wpedantic -Werror
, tapi ini adalah satu-off tes script yang saya lupa untuk memasok argumen untuk.-Wduplicated-cond
bagian dari-Wall
bootstrap GCC yang rusak. Beberapa peringatan yang tampaknya sesuai di sebagian besar kode tidak sesuai di semua kode. Itu sebabnya mereka tidak diaktifkan secara default.int foo() { exit(-1); }
tidak kembaliint
dari fungsi yang "mengklaim untuk mengembalikan int". Ini legal di C ++. Sekarang, itu tidak mengembalikan apa pun ; akhir dari fungsi itu tidak pernah tercapai. Sebenarnya mencapai akhirfoo
akan menjadi perilaku yang tidak terdefinisi. Mengabaikan akhir dari proses kasus,int foo() { throw 3.14; }
juga mengklaim kembaliint
tetapi tidak pernah melakukannya.void* foo(void* arg) { pthread_exit(NULL); }
baik-baik saja untuk alasan yang sama (ketika penggunaannya hanya melaluipthread_create(...,...,foo,...);
)Saya percaya ini karena kode warisan (C tidak pernah meminta pernyataan pengembalian begitu pula C ++). Mungkin ada basis kode besar bergantung pada "fitur" itu. Tetapi setidaknya ada
-Werror=return-type
flag pada banyak compiler (termasuk gcc dan dentang).sumber
Dalam beberapa kasus terbatas dan langka, mengalir dari ujung fungsi yang tidak kosong tanpa mengembalikan nilai bisa berguna. Seperti kode spesifik MSVC berikut:
Fungsi ini mengembalikan pi menggunakan rakitan x86. Tidak seperti perakitan di GCC, saya tahu tidak ada cara
return
untuk melakukan ini tanpa melibatkan overhead dalam hasilnya.Sejauh yang saya tahu, kompiler C ++ mainstream harus memancarkan setidaknya peringatan untuk kode yang tampaknya tidak valid. Jika saya membuat badan
pi()
kosong, GCC / Dentang akan melaporkan peringatan, dan MSVC akan melaporkan kesalahan.Orang menyebutkan pengecualian dan
exit
dalam beberapa jawaban. Itu bukan alasan yang valid. Entah melempar pengecualian, atau memanggilexit
, tidak akan membuat fungsi eksekusi mengalir pada akhirnya. Dan kompiler tahu itu: menulis pernyataan melempar atau memanggilexit
dalam tubuh kosongpi()
akan menghentikan peringatan atau kesalahan dari kompiler.sumber
void
fungsi setelah inline asm meninggalkan nilai dalam register nilai-kembali. (x87st0
dalam hal ini, EAX untuk integer. Dan mungkin xmm0 dalam konvensi pemanggilan yang mengembalikan float / double di xmm0 bukannya st0). Mendefinisikan perilaku ini khusus untuk MSVC; bahkan tidak bergantung pada-fasm-blocks
untuk mendukung sintaks yang sama membuat ini aman. Lihat Apakah __asme {}; mengembalikan nilai eax?Dalam keadaan apa itu tidak menghasilkan kesalahan? Jika ini menyatakan tipe kembali dan tidak mengembalikan sesuatu, itu terdengar seperti kesalahan bagi saya.
Satu-satunya pengecualian yang dapat saya pikirkan adalah
main()
fungsi, yang tidak memerlukanreturn
pernyataan sama sekali (setidaknya dalam C ++; Saya tidak memiliki salah satu standar C yang berguna). Jika tidak ada pengembalian, itu akan bertindak seolah-olahreturn 0;
adalah pernyataan terakhir.sumber
main()
membutuhkan nilaireturn
C.return <value>;
pernyataanreturn 0;
di akhirmain()
(danmain()
hanya). Tapireturn 0;
toh itu gaya yang bagus untuk ditambahkan .Sepertinya Anda perlu mengaktifkan peringatan kompiler Anda:
sumber
return;
dalam fungsi non-void, dan ini merupakan pelanggaran kendala untuk digunakanreturn <value>;
dalam fungsi void. Tapi itu, saya percaya, bukan topiknya. OP, seperti yang saya pahami, adalah tentang keluar dari fungsi non-void tanpareturn
(hanya memungkinkan kontrol mengalir dari akhir fungsi). Ini bukan pelanggaran kendala, AFAIK. Standar hanya mengatakan itu selalu UB dalam C ++ dan kadang-kadang UB dalam C.Ini adalah pelanggaran kendala pada c99, tetapi tidak pada c89. Kontras:
c89:
c99:
Bahkan dalam
--std=c99
mode, gcc hanya akan memberikan peringatan (meskipun tanpa perlu mengaktifkan-W
flag tambahan , seperti yang diharuskan secara default atau dalam c89 / 90).Edit untuk menambahkan bahwa di c89, "mencapai
}
yang mengakhiri fungsi sama dengan mengeksekusireturn
pernyataan tanpa ekspresi" (3.6.6.4). Namun, dalam c99 perilaku tidak terdefinisi (6.9.1).sumber
return
pernyataan eksplisit . Itu tidak mencakup jatuh dari akhir fungsi tanpa mengembalikan nilai.