লিনাক্স শৈলী গাইড আপনার ব্যবহারের নির্দিষ্ট কারণগুলি দেয় 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));
সীমানা ছাড়তে। তবে যত তাড়াতাড়ি সম্ভব দ্রুত হওয়া এবং অপ্রয়োজনীয় ফাংশন কলগুলি এড়াতে, তারা এটি এভাবে করে।
আমি ডিজকস্ট্রার কাগজটি পড়েছি এবং এটি বেশ নিখুঁত বলে মনে করি।
গুগল "ডিজজস্ট্র গোটো বিবৃতি ক্ষতিকারক হিসাবে বিবেচিত" কারণ আমার 2 টির বেশি লিঙ্ক পোস্ট করার মতো খ্যাতি নেই।
আমি এটিকে গোটো ব্যবহার না করার কারণ হিসাবে উদ্ধৃত করেছি এবং এটি পড়ে আমার গোটো ব্যবহারগুলি যেভাবে গ্রহণ করা যায় তেমন কোনও পরিবর্তন হয়নি।
সংযোজন :
এই সমস্ত অবিরত এবং বিরতি সম্পর্কে চিন্তা করতে গিয়ে আমি একটি ঝরঝরে নিয়ম নিয়ে এসেছি।
- যদি কিছুক্ষণের মধ্যে লুপ হয় তবে আপনার একটি চালিয়ে যায়, তবে সেই সময়ের লুপটির মূল অংশটি একটি ফাংশন হওয়া উচিত এবং চালিয়ে যাওয়াটি একটি রিটার্ন স্টেটমেন্ট হওয়া উচিত।
- কিছুক্ষণের মধ্যে যদি আপনার একটি ব্রেক স্টেটমেন্ট থাকে, তবে লুপটি নিজেই একটি ফাংশন হওয়া উচিত এবং বিরতিটি একটি রিটার্ন স্টেটমেন্ট হয়ে যায়।
- আপনার যদি উভয় থাকে তবে কিছু ভুল হতে পারে।
স্কোপ সমস্যাগুলির কারণে এটি সর্বদা সম্ভব হয় না তবে আমি খুঁজে পেয়েছি যে এটি করা আমার কোড সম্পর্কে যুক্তিযুক্ত হওয়া আরও সহজ করে তোলে। আমি লক্ষ্য করেছি যে যখনই লুপের বিরতি বা চালিয়ে যাওয়া তখন আমার খারাপ অনুভূতি জোগায়।