SingleFunctionExitPointPlease may not be the best title for such a page, but it is intended to be a counterpoint to the SingleFunctionExitPoint page. I read through the whole page and a surprising number of comments agree that multiple points of exit are fine. Some believe they are preferable. There is a rule of thumb in programming that goes back a long time that states something like this: A function should have a single point of exit. That is not quite how I learned it. The way I learned it was like this: A function should have one point of entry and one point of exit. Rather than the above, I believe a single code block should have one point of entry and one point of exit. A function is a code block, so this holds true for functions. However it also and perhaps more importantly holds true for any other situation where it might be an issue. In languages like BASIC, Fortran, C and Assembly language you can create blocks of code that have multiple points of both entry and exit. Code where this happens is often called 'spaghetti code'. The more points of entry and exit the more difficult it is to debug the code. Eventually, it becomes impossible for any practical purpose. Here is an example of valid code that breaks the rule: int multifun(int n) { int x = n, y=-1; x = n; assignz: if( y < 9 ) { y=y+1; } goto start; assigny: if(y==9) { y = y-1; odd1: if((y/2)*2==y) { if(y>x) {goto assignz;} return(0+x); } } if(x!=y) { if(y ---- ''What value does that return for multifun(2)? For that matter, does it even return?'' Oh goody! A challenge. Let n0 be the initial value of n when multifun() is called. After the initial section is executed, we have n = n0, x = n0, and y = -1. After the assignz section is executed, we have n = n0, x = n0, and y = 0. (-1 is always less than 9 so we always add 1 to it). In start, we split into two cases. * If n0 = 0, then n = 0, x = 0, and y = 0. We go back and execute the assignz section again. Here y is still less than 9 and therefore incremented. We then return to start with n = 0, x = 0, and y = 1. 0 is always not equal to 1 so we execute the assigny section. 1 is not equal to 9, so we fall through. 0 is not equal to 1, so we enter the if statement. 1 is not less than 0, so we fall through to the printf statement. We then print "y:1\n" and return 5. * If n0 != 0, then n = n0, x = n0, and y = 0. 0 is not equal to 9, so we fall through. Since x = n0 != 0 and y = 0, we see that x != y and enter the if statement. Here we split into two cases, ** If n0 < 0, then we print "y:0\n" and return 5. ** If n0 > 0, then we execute the ok section. Here we split into three cases. *** If n0 == 1, we execute the odd1 section with n = 1, x = 1, and y = 0. Since y = 0, (y/2)*2 equals 0 equals y. Also, 0 is not greater than 1, so we return 1. *** If n0 > 1 but <= INT_MAX - 3, we return x + 2. *** Otherwise the behavior is undefined. From this, I would (assuming that we don't care what it does for the cases that were undefined behavior in the original), rewrite multifun as follows. int multifun_11(int n) { int x = 5; if (n < 0) { puts("y:0"); } else if (n == 0) { puts("y:1"); } else if (n == 1) { x = 1; } else { x = n + 2; } return x; } From this we can clearly see that it returns in all cases (except possibly the undefined behavior ones), and multifun(2) return 4. ---- Well, yes, it really is that simple. :) Thanks for a yeoman effort. What you have done here illustrates the point nicely, I think. Your functionally equivalent code can actually be understood without resorting to a compiler. It is now sound, significantly easier to understand, and much less likely to attract bugs when refactored, which I do now (assuming the informational printing was not a required side effect, which it was not): int multifun_12(int n) { int x = 5; if(n>0) { x = n == 1 ? n : n + 2 } return x; } and finally: #define multifun(n) (n > 0 ? n == 1 ? n : n + 2 : 5) That last refactoring could put a compiler in a significantly better position to optimize code because it eliminates a function call and gives the compiler access so it can tell if surrounding code can eliminate that bit of code entirely. This also illustrates one of the beauties of pair programming. Whether or not you could have gone this last bit, or I could have done your part, neither of us bothered to go the route alone. Together we have produced a beautiful illustration of the utility of the single point rule. Cheers! --BobTrower ''Your welcome. However, your last transformation is one I would strongly recommend against. For one thing, you've changed the functionality. For example, the macro version of multifun(x++) (where x is 7 going in) returns 11 and sets x to 10. The function version returns 9 and sets x to 8. Another example, in the function case multifun(7.5) evaluates to 9. In the macro case, it evaluates to 9.5. Leave it as the function multifun_12, or, if you have access to c99 or later.'' inline int multifun_13(int n) { return n > 0 ? (n == 1 ? n : n + 2) : 5; } ---- We come to another teachable moment. I tested the macro with the code below and it worked fine. I did not test it using the construct you use because I consider it poor practice generally to do what you are doing. I also generally discourage abusing macros like I did here. What we see here, is that: I broke a rule by creating a macro with a possible pathway to failure. Macros like this are prone to certain failure modes and in fact my first crack at that macro failed because of missing brackets. I caught that in testing and corrected that particular defect. I did not exercise all use cases and got caught. I did not catch an error even though I unit tested it, because I did not expect it would be used as it was. Rules of thumb are one of the ways we ensure against failed expectations. I also broke a rule by pointlessly over-optimizing something that was working well enough, simply to be clever. As we can see, clever code is fragile and the fragility can be difficult to see. Code clever enough to break is distinctly less clever than it might seem. The bread and butter code may take a little more time to execute, but it is a small price to pay to ensure a correct answer. I cannot argue that my practice was a good one because you exposed a perfectly plausible mode of failure. It is precisely this unexpected use case where the code broke and breakage is enemy number one. I messed up and the proof is plain to see. Being able to explain breakage does not excuse breakage. I have no doubt that in the fullness of time, if that macro had been in production, it would have failed. I might try to defend the above by saying that this is only example code, but that does not make it better. It makes it worse. Failing example code, unless we do a post-mortem like this, sets a poor example. It is still an example of what *not* to do with production code. I would never actually do that with production code in my day job, but I did it here and breakage is breakage. It is generally poor practice to do what I did with that macro for reasons that you render apparent. It is also generally poor practice to do what you did, also for reasons that you render apparent. Had either of us practiced 100% good hygiene, the breakage, while possible with poor practice when we both transgress, would not occur. This exercise points up the value of testing and the limits of testing. Automated Unit test is necessary, but it is not sufficient. The programmer doing development is not the best person to do the testing. It is often not possible to have the luxury of a second pair of eyes. That is why pair programming is advantageous. In the best case, the testing team is a separate entity. I came to the page that this is responding to because a programmer that reports to me indulged in the bad habit of exiting in the middle of a function. He made a mistake. I came here looking for a tidy explanation of a vanilla rule of thumb. I was surprise to find a lively discussion where there were people in favor of gleefully exercising this bad habit. Worse, it actually seems that they were winning the debate. Our exercise here is largely academic since this is not production code. However, in the real world case of the programmer in question, he ignored this rule and in fact got bitten. The line that I spotted was exiting after resources had been allocated from the heap. It is the type of thing that is unlikely to be caught in ordinary testing. By itself, it would not cause too much harm, but if all the code in the system is making errors like this, the system would suffer badly. The programmer is a good one with a few years behind him. He is just finishing a degree in engineering at Waterloo. He got into this bind because, lacking the experience to know otherwise, he borrowed constructs from other programmers. He did not know this rule of thumb, followed the example of someone who did not know or ignored the rule and his first notice that there was an issue was that his manager pointed out a bug in his code. This is not his failing. It is a failing of the wider community of programmers that surround him. Not knowing better, he trusted people who, like the ones on that other page, actually were recommending code that broke the rule. When I pointed the rule out to him and showed the bug that breaking it gave rise to, he understood right away what the issue was. He is an unusually bright young man with a 4.0 grade average and he really sweats the details. If this gives him trouble, you can bet it gives the average programmer trouble. Below is the snippet of code that ran the unit test on the macro to ensure it behaved as it should. It did in this test, but this test ignores a plausible use case and that untested case caused the code to fail. void tmulti(void) { int i; for(i=0;i<10;i++) { printf( "%s unit test -- f1:%2.2i, f2:%2.2i. multifun_11(%i) %s multifun(%i)\n", multifun_11(i) == multifun_13(i) ? "pass" : "fail", multifun_11(i), multifun_13(i), i, multifun_11(i) == multifun_13(i) ? "==" : "!=", i ); } } Result: pass unit test -- f1:05, f2:05. multifun_11(0) == multifun(0) pass unit test -- f1:01, f2:01. multifun_11(1) == multifun(1) pass unit test -- f1:04, f2:04. multifun_11(2) == multifun(2) pass unit test -- f1:05, f2:05. multifun_11(3) == multifun(3) pass unit test -- f1:06, f2:06. multifun_11(4) == multifun(4) pass unit test -- f1:07, f2:07. multifun_11(5) == multifun(5) pass unit test -- f1:08, f2:08. multifun_11(6) == multifun(6) pass unit test -- f1:09, f2:09. multifun_11(7) == multifun(7) pass unit test -- f1:10, f2:10. multifun_11(8) == multifun(8) pass unit test -- f1:11, f2:11. multifun_11(9) == multifun(9) I could not have hoped for a better unfolding of this page. My mistake was a bona fide mistake and illustrates just how easy it is to fail if you break the rules. Even an expert cannot be certain that a risky move will succeed. I am not sure who my correspondent is on this page, but my thanks goes out to them for making a good page into an excellent one. I expect they must be a pleasure to work with IRL. --- BobTrower