Panduan gaya linux memberikan alasan khusus untuk menggunakan goto
yang sesuai dengan contoh Anda:
https://www.kernel.org/doc/Documentation/process/coding-style.rst
Alasan untuk menggunakan gotos adalah:
- pernyataan tanpa syarat lebih mudah dipahami dan diikuti
- bersarang berkurang
- kesalahan dengan tidak memperbarui titik keluar individu ketika membuat modifikasi dicegah
- menyimpan pekerjaan kompiler untuk mengoptimalkan kode berlebihan;)
Penafian saya tidak seharusnya membagikan pekerjaan saya. Contoh-contoh di sini sedikit dibuat-buat jadi tolong tolong bersamaku.
Ini bagus untuk manajemen memori. Saya baru-baru ini bekerja pada kode yang secara dinamis mengalokasikan memori (misalnya char *
dikembalikan oleh suatu fungsi). Fungsi yang melihat jalur dan memastikan apakah jalur tersebut valid dengan menguraikan token jalur:
tmp_string = strdup(string);
token = strtok(tmp_string,delim);
while( token != NULL ){
...
some statements, some involving dynamically allocated memory
...
if ( check_this() ){
free(var1);
free(var2);
...
free(varN);
return 1;
}
...
some more stuff
...
if(something()){
if ( check_that() ){
free(var1);
free(var2);
...
free(varN);
return 1;
} else {
free(var1);
free(var2);
...
free(varN);
return 0;
}
}
token = strtok(NULL,delim);
}
free(var1);
free(var2);
...
free(varN);
return 1;
Sekarang bagi saya, kode berikut ini jauh lebih baik dan lebih mudah dirawat jika Anda perlu menambahkan varNplus1
:
int retval = 1;
tmp_string = strdup(string);
token = strtok(tmp_string,delim);
while( token != NULL ){
...
some statements, some involving dynamically allocated memory
...
if ( check_this() ){
retval = 1;
goto out_free;
}
...
some more stuff
...
if(something()){
if ( check_that() ){
retval = 1;
goto out_free;
} else {
retval = 0;
goto out_free;
}
}
token = strtok(NULL,delim);
}
out_free:
free(var1);
free(var2);
...
free(varN);
return retval;
Sekarang kode memiliki segala macam masalah lain dengan itu, yaitu bahwa N berada di suatu tempat di atas 10, dan fungsinya lebih dari 450 baris, dengan 10 tingkat bersarang di beberapa tempat.
Tapi saya menawarkan penyelia saya untuk memperbaiki itu, yang saya lakukan dan sekarang banyak fungsi yang semuanya singkat, dan mereka semua memiliki gaya linux
int function(const char * param)
{
int retval = 1;
char * var1 = fcn_that_returns_dynamically_allocated_string(param);
if( var1 == NULL ){
retval = 0;
goto out;
}
if( isValid(var1) ){
retval = some_function(var1);
goto out_free;
}
if( isGood(var1) ){
retval = 0;
goto out_free;
}
out_free:
free(var1);
out:
return retval;
}
Jika kami menganggap yang setara tanpa goto
s:
int function(const char * param)
{
int retval = 1;
char * var1 = fcn_that_returns_dynamically_allocated_string(param);
if( var1 != NULL ){
if( isValid(var1) ){
retval = some_function(var1);
} else {
if( isGood(var1) ){
retval = 0;
}
}
free(var1);
} else {
retval = 0;
}
return retval;
}
Bagi saya, dalam kasus pertama, jelas bagi saya bahwa jika fungsi pertama kembali NULL
, kami keluar dari sini dan kami kembali 0
. Dalam kasus kedua, saya harus gulir ke bawah untuk melihat bahwa if berisi seluruh fungsi. Memang yang pertama menunjukkan ini kepada saya dengan gaya (nama " out
") dan yang kedua melakukannya secara sintaksis. Yang pertama masih lebih jelas.
Juga, saya lebih suka memiliki free()
pernyataan di akhir fungsi. Itu sebagian karena, dalam pengalaman saya, free()
pernyataan di tengah fungsi berbau tidak enak dan menunjukkan kepada saya bahwa saya harus membuat subrutin. Dalam hal ini, saya membuat var1
dalam fungsi saya dan tidak bisa free()
dalam subrutin, tapi itu sebabnya goto out_free
, gaya goto out sangat praktis.
Saya pikir pemrogram perlu dibesarkan dengan keyakinan bahwa goto
itu jahat. Kemudian, ketika mereka sudah cukup dewasa, mereka harus menelusuri kode sumber Linux dan membaca panduan gaya linux.
Saya harus menambahkan bahwa saya menggunakan gaya ini dengan sangat konsisten, setiap fungsi memiliki int retval
, out_free
label dan label out. Karena konsistensi gaya, keterbacaan ditingkatkan.
Bonus: Istirahat dan berlanjut
Katakanlah Anda memiliki loop sementara
char *var1, *var2;
char line[MAX_LINE_LENGTH];
while( sscanf(line,... ){
var1 = functionA(line,count);
var2 = functionB(line,count);
if( functionC(var1, var2){
count++
continue;
}
...
a bunch of statements
...
count++;
free(var1);
free(var2);
}
Ada hal lain yang salah dengan kode ini, tetapi satu hal adalah pernyataan berlanjut. Saya ingin menulis ulang semuanya, tetapi saya ditugaskan untuk memodifikasinya dengan cara yang kecil. Butuh waktu berhari-hari untuk memperbaikinya dengan cara yang memuaskan saya, tetapi perubahan sebenarnya adalah pekerjaan setengah hari. Masalahnya adalah bahwa bahkan jika continue
kita masih harus bebas var1
dan var2
. Saya harus menambahkan var3
, dan itu membuat saya ingin muntah harus mencerminkan pernyataan bebas ().
Saya adalah pekerja magang yang relatif baru pada saat itu, tetapi saya telah melihat kode sumber linux untuk bersenang-senang beberapa waktu lalu, jadi saya bertanya kepada penyelia saya apakah saya bisa menggunakan pernyataan goto. Dia berkata ya, dan saya melakukan ini:
char *var1, *var2;
char line[MAX_LINE_LENGTH];
while( sscanf(line,... ){
var1 = functionA(line,count);
var2 = functionB(line,count);
var3 = newFunction(line,count);
if( functionC(var1, var2){
goto next;
}
...
a bunch of statements
...
next:
count++;
free(var1);
free(var2);
}
Saya pikir terus baik-baik saja tetapi bagi saya itu seperti goto dengan label yang tidak terlihat. Hal yang sama berlaku untuk istirahat. Saya masih lebih suka melanjutkan atau merusak kecuali, seperti yang terjadi di sini, itu memaksa Anda untuk mencerminkan modifikasi di banyak tempat.
Dan saya juga harus menambahkan bahwa penggunaan goto next;
dan next:
label ini tidak memuaskan bagi saya. Mereka hanya lebih baik daripada mencerminkan pernyataan free()
dan count++
pernyataan.
goto
Hampir selalu salah, tetapi orang harus tahu kapan itu baik untuk digunakan.
Satu hal yang tidak saya diskusikan adalah penanganan kesalahan yang telah dicakup oleh jawaban lain.
Performa
Seseorang dapat melihat implementasi strtok () http://opensource.apple.com//source/Libc/Libc-167/string.subproj/strtok.c
#include <stddef.h>
#include <string.h>
char *
strtok(s, delim)
register char *s;
register const char *delim;
{
register char *spanp;
register int c, sc;
char *tok;
static char *last;
if (s == NULL && (s = last) == NULL)
return (NULL);
/*
* Skip (span) leading delimiters (s += strspn(s, delim), sort of).
*/
cont:
c = *s++;
for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
if (c == sc)
goto cont;
}
if (c == 0) { /* no non-delimiter characters */
last = NULL;
return (NULL);
}
tok = s - 1;
/*
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
* Note that delim must have one NUL; we stop if we see that, too.
*/
for (;;) {
c = *s++;
spanp = (char *)delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0)
s = NULL;
else
s[-1] = 0;
last = s;
return (tok);
}
} while (sc != 0);
}
/* NOTREACHED */
}
Harap perbaiki saya jika saya salah, tetapi saya percaya bahwa cont:
label dan goto cont;
pernyataannya ada untuk kinerja (mereka pasti tidak membuat kode lebih mudah dibaca). Mereka dapat diganti dengan kode yang dapat dibaca dengan melakukan
while( isDelim(*s++,delim));
untuk melewati pembatas. Tetapi untuk secepat mungkin dan menghindari pemanggilan fungsi yang tidak perlu, mereka melakukannya dengan cara ini.
Saya membaca koran oleh Dijkstra dan saya merasa cukup esoterik.
google "pernyataan goto dijkstra dianggap berbahaya" karena saya tidak memiliki reputasi yang cukup untuk memposting lebih dari 2 tautan.
Saya telah melihatnya dikutip sebagai alasan untuk tidak menggunakan goto dan membacanya tidak mengubah apa pun sejauh penggunaan goto saya disetujui.
Adendum :
Saya telah datang dengan aturan yang rapi sambil memikirkan semua ini tentang terus dan rusak.
- Jika dalam loop sementara, Anda memiliki lanjutan, maka tubuh loop sementara harus menjadi fungsi dan melanjutkan menjadi pernyataan kembali.
- Jika dalam loop sementara, Anda memiliki pernyataan break, maka loop sementara itu sendiri harus berfungsi dan break harus menjadi pernyataan kembali.
- Jika Anda memiliki keduanya, maka ada sesuatu yang salah.
Itu tidak selalu mungkin karena masalah ruang lingkup tetapi saya telah menemukan bahwa melakukan ini membuatnya lebih mudah untuk alasan tentang kode saya. Saya telah memperhatikan bahwa setiap kali loop sementara istirahat atau melanjutkan itu memberi saya perasaan buruk.