是否应该使用goto语句?
是否应该使用goto语句
goto语句也被称为无条件转移语句,它通常与条件语句配合使用来改变程序流向,使得程序转去执行语句标号所标识的语句。
关于是否应该使用goto语句,历史上也争论不休。恐怕国内大部分教授高级编程语言的课堂上,都会主张在结构化程序设计中不使用goto语句, 以免造成程序流程的混乱,使得理解和调试程序都产生困难。历史上支持goto语句有害的人的主要理由是:goto语句会使程序的静态结构和动态结构不一致,从而使程序难以理解且难以查错。并且G·加科皮尼和C·波姆从理论上证明了:任何程序都可以用顺序、分支和重复结构表示出来。这个结论表明,从高级程序语言中去掉goto语句并不影响高级程序语言的编程能力,而且编写的程序的结构更加清晰。
然而伟大的哲学家黑格尔说过:存在即合理。当笔者刚从校园中走出的时候,对于goto语句有害论也深以为然,然后多年之后在自己编写的代码中随处可见goto的身影。如今很多高级编程语言中,似乎是难以看见goto的身影:Java中不提供goto语句,虽然仍然保留goto为关键字,但不支持它的使用;C#中依然支持goto语句,但是一般不建议使用。其实可以很容易发现一点,这些不提倡使用goto语句的语言,大多是有自带的垃圾回收机制,也就是说不需要过多关心资源的释放的问题,因而在程序流程中没有“为资源释放设置统一出口”的需求。然而对于C++语言来说,程序员需要自己管理资源的分配和释放。倘若没有goto语句,那么我们在某个函数资源分配后的每个出错点需要释放资源并返回结果。虽然我们依然可以不使用goto语句完整地写完流程,但是代码将变得又臭又长。譬如我们需要写一个全局函数g_CreateListenSocket用来创建监听套接字,那么如果不使用goto语句,我们的代码将会是这个样子:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #define MAX_ACCEPT_BACK_LOG 5 void g_CloseSocket(int &nSockfd) { if ( -1 == nSockfd ) { return; } struct linger li = { 1, 0 }; ::setsockopt(nSockfd, SOL_SOCKET, SO_LINGER, (const char *)&li, sizeof(li)); ::close(nSockfd); nSockfd = -1; } in_addr_t g_InetAddr(const char *cszIp) { in_addr_t uAddress = INADDR_ANY; if ( 0 != cszIp && '\0' != cszIp[0] ) { if ( INADDR_NONE == (uAddress = ::inet_addr(cszIp)) ) { uAddress = INADDR_ANY; } } return uAddress; } int g_CreateListenSocket(const char *cszIp, unsigned uPort) { int nOptVal = 1; int nRetCode = 0; int nSocketfd = -1; sockaddr_in saBindAddr; // create a tcp socket nSocketfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if ( -1 == nSocketfd ) { return nSocketfd; } // set address can be reused nRetCode = ::setsockopt(nSocketfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&nOptVal, sizeof(nOptVal)); if ( 0 != nRetCode ) { g_CloseSocket(nSocketfd); return nSocketfd; } // bind address saBindAddr.sin_family = AF_INET; saBindAddr.sin_addr.s_addr = g_InetAddr(cszIp); saBindAddr.sin_port = ::htons(uPort); nRetCode = ::bind(nSocketfd, (struct sockaddr *)&saBindAddr, sizeof(saBindAddr)); if ( 0 != nRetCode ) { g_CloseSocket(nSocketfd); return nSocketfd; } // create a listen socket nRetCode = ::listen(nSocketfd, MAX_ACCEPT_BACK_LOG); if ( 0 != nRetCode ) { g_CloseSocket(nSocketfd); return nSocketfd; } return nSocketfd; }
上面蓝色标记的代码中就包含了出错时候对资源(这里是套接字描述符)进行清理的操作,这里只有单一的资源,所以流程看起来也比较干净。倘若流程中还夹杂着内存分配、打开文件的操作,那么对资源释放操作将变得复杂,不仅代码变得臃肿难看,还不利于对流程的理解。而如果使用了goto语句,那么我们统一为资源释放设定单一出口,那么代码将会是下面这个样子:
int g_CreateListenSocket(const char *cszIp, unsigned uPort) { int nOptVal = 1; int nRetCode = 0; int nSocketfd = -1; sockaddr_in saBindAddr; // create a tcp socket nSocketfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if ( -1 == nSocketfd ) { goto Exit0; } // set address can be reused nRetCode = ::setsockopt(nSocketfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&nOptVal, sizeof(nOptVal)); if ( 0 != nRetCode ) { goto Exit0; } // bind address saBindAddr.sin_family = AF_INET; saBindAddr.sin_addr.s_addr = g_InetAddr(cszIp); saBindAddr.sin_port = ::htons(uPort); nRetCode = ::bind(nSocketfd, (struct sockaddr *)&saBindAddr, sizeof(saBindAddr)); if ( 0 != nRetCode ) { goto Exit0; } // create a listen socket nRetCode = ::listen(nSocketfd, MAX_ACCEPT_BACK_LOG); if ( 0 != nRetCode ) { goto Exit0; } // success here return nSocketfd; Exit0: // fail and clean up resources here if (-1 != nSocketfd) { g_CloseSocket(nSocketfd); } return nSocketfd; }
其实可以发现,加入goto语句之后,流程反而变得清晰了。一个函数将拥有两个出口:执行成功返回和执行失败返回。每次在流程某处出错后都跳转到固定标号处执行资源释放操作,这样在主体流程中将不再出现与资源释放相关的代码,那么主体流程只需专注于逻辑功能,代码将变得更易于理解和维护。另外一个好处就是不容易忘记释放资源,只需要养成分配完一个资源后立即在资源统一释放处编写资源释放代码的好习惯即可,对于程序员复查自己的代码也带来好处。
使用宏来简化代码量
仔细观察上面的代码,再结合前面所言的goto语句通常与条件语句配合使用来改变程序流向,可以总结规律:我们总是检查某个条件是否成立,如果条件不成立立即goto到指定的函数执行失败入口处,那么我们可以设计宏如下:
#undef DISABLE_WARNING #ifdef _MSC_VER // MS VC++ #define DISABLE_WARNING(code, expression) \ pragma(warning(push)) \ pragma(warning(disable:code)) expression \ pragma(warning(pop)) #else // GCC #define DISABLE_WARNING(code, expression) \ expression #endif // _MSC_VER #undef WHILE_FALSE_NO_WARNING #define WHILE_FALSE_NO_WARNING DISABLE_WARNING(4127, while(false)) #undef PROCESS_ERROR_Q #define PROCESS_ERROR_Q(condition) \ do \ { \ if (!(condition)) \ { \ goto Exit0; \ } \ } WHILE_FALSE_NO_WARNING #undef PROCESS_ERROR #define PROCESS_ERROR(condition) \ do \ { \ if (!(condition)) \ { \ assert(false); \ goto Exit0; \ } \ } WHILE_FALSE_NO_WARNING
int g_CreateListenSocket(const char *cszIp, unsigned uPort) { int nOptVal = 1; int nRetCode = 0; int nSocketfd = -1; sockaddr_in saBindAddr; // create a tcp socket nSocketfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); PROCESS_ERROR(-1 != nSocketfd); // set address can be reused nRetCode = ::setsockopt(nSocketfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&nOptVal, sizeof(nOptVal)); PROCESS_ERROR(0 == nRetCode); // bind address saBindAddr.sin_family = AF_INET; saBindAddr.sin_addr.s_addr = g_InetAddr(cszIp); saBindAddr.sin_port = ::htons(uPort); nRetCode = ::bind(nSocketfd, (struct sockaddr *)&saBindAddr, sizeof(saBindAddr)); PROCESS_ERROR(0 == nRetCode); // create a listen socket nRetCode = ::listen(nSocketfd, MAX_ACCEPT_BACK_LOG); PROCESS_ERROR(0 == nRetCode); // success here return nSocketfd; Exit0: // fail and clean up resources here if (-1 != nSocketfd) { g_CloseSocket(nSocketfd); } return nSocketfd; }
统一函数出口
如果想统一函数出口,其实方法很简单:只需要加入一个int nResult字段,初始化为false,在函数流程完全走完时标记为true,然后在释放资源处判断该字段是否为false即可。可以参考下面代码:
int g_CreateListenSocket(const char *cszIp, unsigned uPort) { int nResult = false; int nRetCode = false; int nOptVal = 1; int nSocketfd = -1; sockaddr_in saBindAddr; // create a tcp socket nSocketfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP); PROCESS_ERROR(-1 != nSocketfd); // set address can be reused nRetCode = ::setsockopt(nSocketfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&nOptVal, sizeof(nOptVal)); PROCESS_ERROR(0 == nRetCode); // bind address saBindAddr.sin_family = AF_INET; saBindAddr.sin_addr.s_addr = g_InetAddr(cszIp); saBindAddr.sin_port = ::htons(uPort); nRetCode = ::bind(nSocketfd, (struct sockaddr *)&saBindAddr, sizeof(saBindAddr)); PROCESS_ERROR(0 == nRetCode); // create a listen socket nRetCode = ::listen(nSocketfd, MAX_ACCEPT_BACK_LOG); PROCESS_ERROR(0 == nRetCode); // success here nResult = true; Exit0: // fail and clean up resources here if (!nResult) { if (-1 != nSocketfd) { g_CloseSocket(nSocketfd); } } return nSocketfd; }
测试代码
最后附上上述代码的测试代码:
int main(int argc, char ** argv) { socklen_t nAddrLen = sizeof(struct sockaddr_in); int nListenSocketfd = -1; struct sockaddr_in saRemoteAddr; nListenSocketfd = g_CreateListenSocket("", 9999); if ( -1 == nListenSocketfd ) { return 0; } while (true) { ::memset(&saRemoteAddr, 0, sizeof(saRemoteAddr)); int nSocketfd = ::accept(nListenSocketfd, (struct sockaddr *)&saRemoteAddr, &nAddrLen); ::printf("Accept a new connection from [ip - %s, port - %d]\n", ::inet_ntoa(saRemoteAddr.sin_addr), ::ntohs(saRemoteAddr.sin_port) ); g_CloseSocket(nSocketfd); } return 1; }
Atas ialah kandungan terperinci 是否应该使用goto语句?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas

Pautan magnet ialah kaedah pautan untuk memuat turun sumber, yang lebih mudah dan cekap daripada kaedah muat turun tradisional. Pautan magnet membolehkan anda memuat turun sumber secara peer-to-peer tanpa bergantung pada pelayan perantara. Artikel ini akan memperkenalkan cara menggunakan pautan magnet dan perkara yang perlu diberi perhatian. 1. Apakah pautan magnet? Pautan magnet ialah kaedah muat turun berdasarkan protokol P2P (Peer-to-Peer). Melalui pautan magnet, pengguna boleh menyambung terus kepada penerbit sumber untuk melengkapkan perkongsian dan muat turun sumber. Berbanding dengan kaedah muat turun tradisional, magnet

Cara menggunakan fail mdf dan fail mds Dengan kemajuan teknologi komputer yang berterusan, kami boleh menyimpan dan berkongsi data dalam pelbagai cara. Dalam bidang media digital, kita sering menghadapi beberapa format fail khas. Dalam artikel ini, kami akan membincangkan format fail biasa - fail mdf dan mds, dan memperkenalkan cara menggunakannya. Pertama, kita perlu memahami maksud fail mdf dan fail mds. mdf ialah lanjutan fail imej CD/DVD, dan fail mds ialah fail metadata bagi fail mdf.

CrystalDiskMark ialah alat penanda aras HDD kecil untuk pemacu keras yang cepat mengukur kelajuan baca/tulis berurutan dan rawak. Seterusnya, biarkan editor memperkenalkan CrystalDiskMark kepada anda dan cara menggunakan crystaldiskmark~ 1. Pengenalan kepada CrystalDiskMark CrystalDiskMark ialah alat ujian prestasi cakera yang digunakan secara meluas yang digunakan untuk menilai kelajuan baca dan tulis serta prestasi pemacu keras mekanikal dan pemacu keadaan pepejal (SSD Prestasi I/O rawak. Ia adalah aplikasi Windows percuma dan menyediakan antara muka mesra pengguna dan pelbagai mod ujian untuk menilai aspek prestasi cakera keras yang berbeza dan digunakan secara meluas dalam ulasan perkakasan

foobar2000 ialah perisian yang boleh mendengar sumber muzik pada bila-bila masa Ia membawakan anda semua jenis muzik dengan kualiti bunyi tanpa kehilangan Versi pemain muzik yang dipertingkatkan membolehkan anda mendapatkan pengalaman muzik yang lebih komprehensif dan selesa mainkan audio lanjutan pada komputer Peranti dipindahkan ke telefon mudah alih untuk memberikan pengalaman main balik muzik yang lebih mudah dan cekap Reka bentuk antara muka adalah ringkas, jelas dan mudah digunakan Ia menggunakan gaya reka bentuk minimalis tanpa terlalu banyak hiasan dan operasi yang menyusahkan untuk bermula dengan cepat. Ia juga menyokong pelbagai kulit dan Tema, memperibadikan tetapan mengikut pilihan anda sendiri, dan mencipta pemain muzik eksklusif yang menyokong main balik berbilang format audio. Ia juga menyokong fungsi perolehan audio untuk melaraskan kelantangan kepada keadaan pendengaran anda sendiri untuk mengelakkan kerosakan pendengaran yang disebabkan oleh kelantangan yang berlebihan. Seterusnya, izinkan saya membantu anda

Storan awan telah menjadi bahagian yang amat diperlukan dalam kehidupan dan kerja harian kita pada masa kini. Sebagai salah satu perkhidmatan storan awan terkemuka di China, Baidu Netdisk telah memenangi hati sebilangan besar pengguna dengan fungsi storan yang berkuasa, kelajuan penghantaran yang cekap dan pengalaman operasi yang mudah. Dan sama ada anda ingin menyandarkan fail penting, berkongsi maklumat, menonton video dalam talian atau mendengar muzik, Baidu Cloud Disk boleh memenuhi keperluan anda. Walau bagaimanapun, ramai pengguna mungkin tidak memahami penggunaan khusus aplikasi Baidu Netdisk, jadi tutorial ini akan memperkenalkan anda tentang cara menggunakan aplikasi Baidu Netdisk secara terperinci Jika anda masih keliru, sila ikuti artikel ini untuk mengetahui lebih lanjut! Cara menggunakan Cakera Rangkaian Awan Baidu: 1. Pemasangan Mula-mula, semasa memuat turun dan memasang perisian Baidu Cloud, sila pilih pilihan pemasangan tersuai.

NetEase Mailbox, sebagai alamat e-mel yang digunakan secara meluas oleh netizen Cina, sentiasa memenangi kepercayaan pengguna dengan perkhidmatannya yang stabil dan cekap. NetEase Mailbox Master ialah perisian e-mel yang dicipta khas untuk pengguna telefon mudah alih. Ia sangat memudahkan proses menghantar dan menerima e-mel dan menjadikan pemprosesan e-mel kami lebih mudah. Jadi bagaimana untuk menggunakan NetEase Mailbox Master, dan apakah fungsi khusus yang ada di bawah, editor tapak ini akan memberi anda pengenalan terperinci, dengan harapan dapat membantu anda. Mula-mula, anda boleh mencari dan memuat turun aplikasi NetEase Mailbox Master di gedung aplikasi mudah alih. Cari "Induk Peti Mel NetEase" dalam App Store atau Baidu Mobile Assistant, dan kemudian ikut gesaan untuk memasangnya. Selepas muat turun dan pemasangan selesai, kami membuka akaun e-mel NetEase dan log masuk. Antara muka log masuk adalah seperti yang ditunjukkan di bawah

Selepas lama menekan butang main pembesar suara, sambungkan ke wifi dalam perisian dan anda boleh menggunakannya. Tutorial Model Berkenaan: Xiaomi 12 Sistem: EMUI11.0 Versi: Xiaoai Classmate 2.4.21 Analisis 1 Mula-mula cari butang main pembesar suara, dan tekan dan tahan untuk memasuki mod pengedaran rangkaian. 2 Log masuk ke akaun Xiaomi anda dalam perisian Xiaoai Speaker pada telefon anda dan klik untuk menambah Speaker Xiaoai baharu. 3. Selepas memasukkan nama dan kata laluan wifi, anda boleh menghubungi Xiao Ai untuk menggunakannya. Tambahan: Apakah fungsi yang ada pada Xiaoai Speaker 1 Xiaoai Speaker mempunyai fungsi sistem, fungsi sosial, fungsi hiburan, fungsi pengetahuan, fungsi kehidupan, rumah pintar dan rancangan latihan. Ringkasan/Nota: Apl Xiao Ai mesti dipasang pada telefon mudah alih anda terlebih dahulu untuk sambungan dan penggunaan yang mudah.

Bermula dengan mudah: Cara menggunakan sumber cermin pip Dengan populariti Python di seluruh dunia, pip telah menjadi alat standard untuk pengurusan pakej Python. Walau bagaimanapun, masalah biasa yang dihadapi oleh ramai pembangun apabila menggunakan pip untuk memasang pakej adalah kelambatan. Ini kerana secara lalai, pip memuat turun pakej daripada sumber rasmi Python atau sumber luaran lain, dan sumber ini mungkin terdapat pada pelayan luar negara, menyebabkan kelajuan muat turun yang perlahan. Untuk meningkatkan kelajuan muat turun, kami boleh menggunakan sumber cermin pip. Apakah sumber cermin pip? Secara ringkasnya, hanya
