लिनक्स शैली मार्गदर्शिका विशिष्ट कारणों का उपयोग करती है goto
जो आपके उदाहरण के अनुरूप हैं:
https://www.kernel.org/doc/Documentation/process/coding-style.rst
गोटो का उपयोग करने का औचित्य है:
- बिना शर्त बयान समझने और अनुसरण करने में आसान होते हैं
- घोंसला बनाना कम हो जाता है
- संशोधनों को रोकने पर व्यक्तिगत निकास बिंदुओं को अपडेट नहीं करने से त्रुटियां होती हैं
- कंपाइलर काम को निरर्थक कोड को दूर करने के लिए बचाता है;)
अस्वीकरण मैं अपना काम साझा करने वाला नहीं हूं। यहाँ के उदाहरण थोड़े वंचित हैं इसलिए कृपया मेरे साथ सहन करें।
यह स्मृति प्रबंधन के लिए अच्छा है। मैंने हाल ही में कोड पर काम किया था जिसे गतिशील रूप से मेमोरी आवंटित की गई थी (उदाहरण के लिए char *
फ़ंक्शन द्वारा लौटाया गया)। एक फ़ंक्शन जो पथ को देखता है और यह पता लगाता है कि पथ के टोकन को पार्स करके पथ मान्य है या नहीं:
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;
अब मेरे लिए, निम्न कोड बहुत अच्छा है और अगर आप को जोड़ने की आवश्यकता है, तो इसे बनाए रखना आसान है 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;
अब कोड में इसके साथ सभी प्रकार की अन्य समस्याएं थीं, अर्थात् एन कहीं 10 से ऊपर था, और फ़ंक्शन 450 लाइनों से अधिक था, जिसमें कुछ स्थानों पर 10 स्तर की नेस्टीनेस थी।
लेकिन मैंने अपने पर्यवेक्षक को इसे रिफैक्ट करने की पेशकश की, जो मैंने किया और अब यह उन कार्यों का एक गुच्छा है जो सभी छोटे हैं, और उन सभी में लिनक्स शैली है
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
एस के बिना समकक्ष मानते हैं :
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;
}
मेरे लिए, पहले मामले में, मेरे लिए यह स्पष्ट है कि यदि पहला फ़ंक्शन वापस आता है NULL
, तो हम यहाँ से बाहर निकल रहे हैं और हम लौट रहे हैं 0
। दूसरे मामले में, मुझे यह देखने के लिए नीचे स्क्रॉल करना होगा कि क्या पूरे फ़ंक्शन में है। पहले वाले ने मुझे शैलीगत रूप से (" out
" नाम ) को इंगित किया और दूसरा इसे वाक्यगत रूप से दर्शाता है । पहला वाला अभी भी अधिक स्पष्ट है।
इसके अलावा, मैं free()
एक समारोह के अंत में बयान देना पसंद करता हूं । यह आंशिक रूप से है, क्योंकि मेरे अनुभव में, free()
कार्यों के बीच में बयानों से बदबू आती है और मुझे संकेत मिलता है कि मुझे एक सबरूटीन बनाना चाहिए। इस मामले में, मैंने var1
अपने फ़ंक्शन में बनाया और free()
इसे एक सबरूटीन में नहीं कर सकता था , लेकिन यही कारण है कि goto out_free
, गोटो आउट शैली इतनी व्यावहारिक है।
मुझे लगता है कि प्रोग्रामर को यह विश्वास दिलाने की जरूरत है कि goto
बुराई है। फिर, जब वे पर्याप्त परिपक्व हो जाते हैं, तो उन्हें लिनक्स स्रोत कोड ब्राउज़ करना चाहिए और लिनक्स शैली गाइड पढ़ना चाहिए।
मुझे यह जोड़ना चाहिए कि मैं इस शैली का लगातार उपयोग करता हूं, प्रत्येक फ़ंक्शन में एक इंट retval
, एक out_free
लेबल और एक आउट लेबल होता है। शैलीगत स्थिरता के कारण, पठनीयता में सुधार हुआ है।
बोनस: टूटता है और जारी रहता है
कहते हैं कि आपके पास कुछ समय है
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);
}
इस कोड के साथ अन्य चीजें भी गलत हैं, लेकिन एक बात जारी बयान है। मैं पूरी बात को फिर से लिखना चाहूंगा, लेकिन मुझे इसे छोटे तरीके से संशोधित करने का काम सौंपा गया था। मुझे इसे फिर से शुरू करने में कुछ दिन लगेंगे, जो मुझे संतुष्ट करेगा, लेकिन वास्तविक बदलाव आधे दिन के काम के बारे में था। समस्या भले ही हम यह है कि continue
'हम अभी भी मुक्त करने की जरूरत है var1
और var2
। मुझे एक जोड़ना था var3
, और इसने मुझे मुक्त () कथनों को आईने में ढालने के लिए प्रेरित किया।
मैं उस समय एक अपेक्षाकृत नया प्रशिक्षु था, लेकिन मैं कुछ समय पहले मस्ती के लिए लिनक्स स्रोत कोड देख रहा था, इसलिए मैंने अपने पर्यवेक्षक से पूछा कि क्या मैं एक गोटो बयान का उपयोग कर सकता हूं। उसने कहा हाँ, और मैंने ऐसा किया:
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);
}
मुझे लगता है कि जारी रखना ठीक है, लेकिन मेरे लिए वे एक अदृश्य लेबल के साथ एक गोटो की तरह हैं। वही टूट जाता है। मैं तब भी जारी रखना या तोड़ना पसंद करूंगा जब तक कि यहाँ मामला नहीं था, यह आपको कई स्थानों पर संशोधन करने के लिए मजबूर करता है।
और मुझे यह भी जोड़ना चाहिए कि यह goto next;
और next:
लेबल का उपयोग मेरे लिए असंतोषजनक है। वे केवल बयान free()
और count++
बयानों को प्रतिबिंबित करने से बेहतर हैं ।
goto
लगभग हमेशा गलत होते हैं, लेकिन किसी को पता होना चाहिए कि वे कब उपयोग करना अच्छा है।
एक चीज जिस पर मैंने चर्चा नहीं की, वह है त्रुटि हैंडलिंग जो अन्य उत्तरों द्वारा कवर की गई है।
प्रदर्शन
स्ट्रेटोक के कार्यान्वयन को देख सकते हैं () 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 */
}
कृपया मुझे सही करें अगर मैं गलत हूं, लेकिन मेरा मानना है कि cont:
लेबल और goto cont;
स्टेटमेंट प्रदर्शन के लिए हैं (वे निश्चित रूप से कोड को अधिक पठनीय नहीं बनाते हैं)। उन्हें ऐसा करने से पठनीय कोड से बदला जा सकता है
while( isDelim(*s++,delim));
सीमांकक छोड़ने के लिए। लेकिन जितनी जल्दी हो सके और अनावश्यक फ़ंक्शन कॉल से बचने के लिए, वे इस तरह से करते हैं।
मैंने पेपर को दिज्क्स्ट्रा द्वारा पढ़ा और मुझे यह काफी गूढ़ लगा।
Google "dijkstra गोटो स्टेटमेंट को हानिकारक माना जाता है" क्योंकि मेरे पास 2 से अधिक लिंक पोस्ट करने के लिए पर्याप्त प्रतिष्ठा नहीं है।
मैंने इसे गोटो के इस्तेमाल न करने के एक कारण के रूप में उद्धृत किया है और इसे पढ़ने से कुछ भी नहीं बदला है क्योंकि गोटो के मेरे उपयोगों को छुपाया गया है।
परिशिष्ट :
मैं एक साफ नियम के साथ आया हूं जबकि यह सब जारी है और टूटता है।
- यदि थोड़ी देर के लूप में, आपके पास जारी रहता है, तो जबकि लूप का शरीर एक फ़ंक्शन होना चाहिए और जारी एक रिटर्न स्टेटमेंट होना चाहिए।
- यदि थोड़ी देर के लूप में, आपके पास एक ब्रेक स्टेटमेंट है, तो जबकि लूप स्वयं एक फ़ंक्शन होना चाहिए और ब्रेक एक रिटर्न स्टेटमेंट बनना चाहिए।
- अगर आपके पास दोनों हैं, तो कुछ गलत हो सकता है।
स्कोप के मुद्दों के कारण यह हमेशा संभव नहीं है, लेकिन मैंने पाया है कि ऐसा करने से मेरे कोड के बारे में तर्क करना बहुत आसान हो जाता है। मैंने देखा था कि जब भी कुछ समय के लिए लूप में ब्रेक होता था या जारी रहता था, तो इससे मुझे बुरा एहसास होता था।