Linux stili kılavuzu goto
, örneğinize uygun olarak kullanmak için belirli nedenler sunar :
https://www.kernel.org/doc/Documentation/process/coding-style.rst
Gotos kullanmanın mantığı şudur:
- koşulsuz ifadelerin anlaşılması ve takip edilmesi daha kolaydır
- yuvalama azaltılır
- Değişiklik yaparken bireysel çıkış noktalarını güncellememekle hatalar önlenir
- gereksiz kodu uzağa optimize etmek için derleyici çalışmasını kaydeder;)
Sorumluluk reddi Çalışmalarımı paylaşmamam gerekiyor. Buradaki örnekler biraz tartışmalı, bu nedenle lütfen benimle birlikte taşıyın.
Bu hafıza yönetimi için iyidir. Geçenlerde dinamik olarak ayrılmış hafızaya sahip kod üzerinde çalıştım (örneğin char *
bir fonksiyon tarafından döndürüldü). Yola bakan ve yolun belirteçlerini ayrıştırıp yolun geçerli olup olmadığını belirleyen bir işlev:
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;
Şimdi bana göre, aşağıdaki kodu eklemeniz gerekiyorsa, bakımı daha kolay ve kolaydır 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;
Şimdi kodda N ile 10'un üzerinde bir yerdeydi ve fonksiyonun 450 satırdan fazla olduğu, bazı yerlerde 10 iç içe geçmişlikten başka bir sorun yoktu.
Ama amirime refactor yapmasını teklif ettim, yaptığım ve şimdi hepsi kısa olan bir sürü fonksiyon ve hepsinde linux tarzı var.
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;
}
goto
S olmadan eşdeğerini düşünürsek :
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;
}
Bana göre, ilk durumda, ilk işlev dönerse NULL
, buradan çıkacağımız ve geri döndüğüm bana açıktır 0
. İkinci durumda, eğer tüm işlevi içeriyorsa görmek için aşağı kaydırmam gerekir. İlk verilen bana bunu stilistik olarak gösterir (" out
" adı ) ve ikincisi sözdizimsel olarak yapar. İlki hala daha açık.
Ayrıca, free()
bir fonksiyonun sonunda ifadeleri tercih ederim . Bu kısmen, deneyimlerime göre, free()
işlevlerin ortasındaki ifadeler kötü kokuyor ve bana bir alt program oluşturmam gerektiğini gösteriyor. Bu durumda, var1
fonksiyonumda yarattım ve bunu free()
bir alt programda yapamadım , ama bu yüzden, goto out_free
dışarı çıkma tarzı bu kadar pratik.
Bence programcıların bunun goto
kötü olduğuna inanmakla yetinmek zorunda . Sonra, yeterince olgun olduklarında, Linux kaynak koduna göz atmalı ve linux stil kılavuzunu okumalılar.
Bu stili çok tutarlı kullandığımı eklemeliyim, her fonksiyonun bir int retval
, bir out_free
etiketi ve bir çıkışı vardır. Stilistik tutarlılık nedeniyle okunabilirlik artırıldı.
Bonus: Sonları ve devam ediyor
Bir süre döngü olduğunu söyle
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);
}
Bu kodda yanlış olan başka şeyler var, ancak bunlardan biri, devamı ifadesidir. Her şeyi yeniden yazmak istiyorum, ancak küçük bir şekilde değiştirmekle görevlendirildim. Beni memnun edecek bir şekilde yeniden canlandırmam günler alırdı, ancak asıl değişiklik yaklaşık yarım günlük bir işti. Sorun biz 'bile olmasıdır continue
biz hala serbest gerekiyor' var1
ve var2
. Bir tane eklemek zorunda kaldım var3
ve bu da free () ifadelerini yansıtmak zorunda kalmam için kusmama neden oldu.
O zamanlar nispeten yeni bir stajyerdim, ancak bir süre önce linux kaynak koduna bakıyordum, bu yüzden amirime bir goto ifadesi kullanıp kullanamayacağımı sordum. Evet dedi ve ben bunu yaptım:
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);
}
Bence en iyisi devam ediyor ama benim için görünmez bir etiketi var. Aynı molalar için de geçerli. Burada olduğu gibi, sizi birçok yerde değişiklik yapmaya zorlamadığı sürece, devam etmeyi veya kırılmayı tercih ederim.
Ayrıca şunu da eklemeliyim ki, bu kullanımın goto next;
ve next:
etiketin benim için tatmin edici olmadığını. Onlar yansıtma daha sadece iyidir free()
's ve count++
ifadeleri.
goto
Neredeyse her zaman yanlıştır, ancak ne zaman kullanmanın iyi olduğunu bilmek gerekir.
Tartışmadığım bir şey, diğer cevapların kapsadığı hata yönetimi.
Verim
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 */
}
Lütfen hatalıysam beni düzelt, ancak cont:
etiketin ve goto cont;
ifadenin performans için orada olduğuna inanıyorum (kesinlikle kodu daha okunaklı hale getirmiyorlar). Yaparak okunabilir kod ile değiştirilebilir
while( isDelim(*s++,delim));
sınırlayıcıları atlamak için. Ancak olabildiğince hızlı olmak ve gereksiz işlev çağrıları yapmaktan kaçınmak için bunu bu şekilde yaparlar.
Makaleyi Dijkstra'dan okudum ve çok ezoterik buldum.
google "dijkstra goto bildirimi zararlı olarak değerlendirildi" çünkü 2'den fazla bağlantı gönderecek kadar itibarım yok.
Goto'ları kullanmamak için bir neden olarak alıntı yaptığımı ve goto'ların kullanımları birleştiğinde okuduğumda hiçbir şeyin değişmediğini gördüm.
Zeyilname :
Devam eden ve molalar hakkındaki her şeyi düşünürken temiz bir kural ile geldim.
- Bir süre döngüsünde devam ederseniz, süre döngüsünün gövdesi bir işlev olmalı ve devam bir return ifadesi olmalıdır.
- Bir süre döngüsünde, bir break ifadesine sahipseniz, while döngüsünün kendisi bir işlev olmalı ve break bir return deyimi olmalıdır.
- Her ikisine de sahipseniz, bir şeyler yanlış olabilir.
Kapsam sorunları nedeniyle her zaman mümkün olmamakla birlikte, bunu yapmanın kodumla ilgili mantıklı olmayı çok daha kolay hale getirdiğini gördüm. Ne zaman bir döngü bir mola veya devam ettiğinde bana kötü bir his verdiğini fark etmiştim.