Whether goto statements should be used
A goto statement is also known as an unconditional transfer statement , which is usually used in conjunction with a conditional statement to change the flow of the program, allowing the program to go through the statements identified by the statement label .
Historically, there has been a debate over whether GOTO statements should be used. I am afraid that most of the classes in the domestic teaching of high-level programming language, will be advocated in structured programming without the use of goto statements, so as to avoid the confusion of the process, making it difficult to understand and debug the program. The main reason why historically the people who support goto statements to be harmful is thatgoto statements make the static structure and dynamic structure of the program inconsistent , making the program difficult to understand and difficult to find . and G. Gacoppigny and C. Bohm theoretically prove that any program can be expressed in order, branching and repeating structures . This conclusion shows that the removal of the Goto statement from the Advanced programming language does not affect the programming ability of the high-level program language, and the structure of the written program is clearer.
existence is reasonable . When the author just walked out of the campus, for the goto statement harmful theory also deep thought, and then many years later in their own code to write everywhere goto figure. In many high-level programming languages, it seems difficult to see Goto: There is no goto statement in Java, although the Goto keyword is still reserved, but it is not supported, and the GOTO statement is still supported in C #, but is generally not recommended. In fact, it is easy to find that these languages, which do not advocate the use of goto statements, are mostly self-contained garbage collection mechanisms , which means that there is no need to worry too much about the release of resources, so there is no " the requirement to set the unified egress for resource release . For the C + + language, however, programmers need to manage the allocation and release of resources themselves. Without a goto statement, we need to release the resource and return the result at each error point after the allocation of a function resource. Although we can still complete the process without using the goto statement, the code will become smelly and long. For example, we need to write a global function G_createlistensocket to create a listening socket, so if we don't use the goto statement, our code will look like this:
#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 5void 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 && ' + '! = 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 Liste N Socket nretcode =:: Listen (NSOCKETFD, max_accept_back_log); if (0! = nRetCode) { G_closesocket (NSOCKETFD); return NSOCKETFD; } return NSOCKETFD;}
The blue tag above contains an operation to clean up the resource (this is the socket descriptor) at the time of the error, where there is only a single resource, So the process looks pretty clean too. If the process is also mixed with memory allocation, open file operation, then the resource release operations will become complex, not only the code is bloated and ugly, but also not conducive to the understanding of the process. And if we use a goto statement, then we Unified set single egress for resource release , then the code would look like this:
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 (n SOCKETFD, 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;}
You can actually find that the process becomes clearer after you add a goto statement. A function will have two exits: Perform a successful return and perform a failed return. Each time an error occurs in the process, it jumps to the fixed label to perform the resource release operation, so that the code associated with the resource release will no longer appear in the main process, so the main process needs to focus on the logical function and the code becomes easier to understand and maintain. Another benefit is that it's not easy to forget to release resources, just get into the habit of writing resource release code immediately after allocating a resource, and it's also good for programmers to review their code.
Using macros to simplify the amount of code
Careful observation of the above code, combined with the aforementioned goto statement is usually used in conjunction with conditional statements to change the flow of the program, you can summarize the rule: we always check whether a condition is true, if the condition is not immediately goto to the specified function failed entrance, then we can design the macro as follows:
#undef disable_warning#ifdef _msc_ver//MS VC + + #define Disable_w Arning (code, expression) \ pragma (warning (push)) \ pragma (Warning (disable:code)) E xpression \ pragma (Warning (pop)) #else//Gcc#def Ine disable_warning (code, expression) \ expression#endif//_msc_ver#undef while_false_no_warning#define WHI Le_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_e Rror#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, S Izeof (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;}
Unified function Exit
If you want to unify the function exit, the method is simple: simply add an int nresult field, initialize to False, Mark true when the function flow is complete, and then determine if the field is false at the release resource. You can refer to the following code:
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, S Izeof (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;}
Test code
Finally, we enclose the test code of the above code:
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); ::p rintf ("Accept A new connection from [IP-%s, Port-%d]\n, :: Inet_ntoa (SAREMOTEADDR.SIN_ADDR), :: Ntohs (Saremoteaddr.sin_ Port) ); G_closesocket (NSOCKETFD); } return 1;}