Prev: ready to run 32bit controller
Next: ARM7
From: Grant Edwards on 2 Jun 2010 11:26 On 2010-06-02, hamilton <hamilton(a)nothere.com> wrote: > On 6/2/2010 9:12 AM, D Yuniskis wrote: >> hamilton wrote: >>>> Using assembly I would have used "goto" to steer execution where I >>>> wanted >>>> it, but how is it approached in C? >>> >>> There is no "goto" in C. >>> >>> "goto" in C is bad (very bad) practice. >> >> (sigh) No, goto has a role in C. What is wrong is using >> goto as if it was GOTO (e.g., BASIC). >> >> Usually, you can restructure your code with suitable blocks >> to eliminate the need for a "goto". But, there are times >> when "goto" is the *right* choice. Convincing yourself >> that it is "very bad practice" will blind you to when >> it is the *right* practice! > > As you have stated "you can restructure your code". There are times when a goto is simpley the best choice and results in much simpler and eaiser to maintain code. > Why change your code just to use a goto ?? No idea what you're asking. > Design your code right the first time. ;-) Sometimes a goto is the right way. -- Grant Edwards grant.b.edwards Yow! Go on, EMOTE! at I was RAISED on thought gmail.com balloons!!
From: D Yuniskis on 2 Jun 2010 11:37 Hi Royston, Royston Vasey wrote: > I've got a very basic question guys, I'm teaching myself C using Microchip's > C18 compiler. Great! > I've got my main loop running, looking for certain conditions to occur, > receiving a serial command & processing it. > > I need to monitor a few things for a time out condition, to do this I'm > pre-loading Timer1 with the required values and when the timer overflows it > jumps to the relevant interrupt. What are you timing? I.e., are you trying to prevent the code from waiting for a received character indefinitely? Or, trying to abort a system call after a certain time limit is exceeded? etc. > All the above works fine. > > When the Timer1 interrupt fires what is the easiest/best/correct way to > execute a block of "reset" code and then recommence execution at a > _particular_ place in the main() loop? <frown> You (typically) can't just jump around your code "willy nilly" -- unless you really know what's going on under the hood (doubtful if you're just learning). E.g., when the IRQ is signalled, *something* was being done at that time. That "something" has to be considered when you decide to *abandon* it (by jumping to a *particular* place in the main() loop). If, for example, the code was busy *deep* in some nested function call -- lots of cruft on the stack -- and you just "jumped" to someplace else to "resume execution", then you have all that cruft still sitting on the stack with nothing to unroll it. (what happens if the *next* interrupt does the exact same thing? and the one after that? i.e., you eventually "blow the stack" because you've never cleaned its previous contents off of it) Even if you were lucky enough to be able to perform the jump without consequence, you can't jump to any arbitrary point in "main()" -- because you don't know what the prerequisites for that part of main happen to be. Consider, what would you expect the code to do if you jumped into the middle of a "for" loop? If you are building an application that relies on a simple while loop for it's structure, then you have to poll every "event" of interest in that loop and "dispatch" accordingly *from* that loop. Your interrupt has to be "invisible" to the application (in terms of how it *immediately* affects program flow). If you want to implement a simple timeout, try something like: - set a (volatile) flag (*bit*!) someplace "common" - have the ISR *clear* that flag when it occurs - have whatever "service" you are trying to "time" poll that flag along with whatever else it is supposed to do; if it sees the flag get cleared "magically", the service returns an error (TIMEOUT) instead of success So, if you wanted to put a timeout on how long you would wait for a character to be received: result receive_character(char *character, boolean *flag) { while (*flag == SET) { if (check_UART_for_character() == TRUE) { *character = get_UART_character(); return SUCCESS; } return FAILURE; } I.e., as long as the flag is still *set*, keep checking the UART to see if a character is available. If so, return the character *and* indicate "SUCCESS". However, once the flag is NOT "SET", stop checking the UART and just return a FAILURE (TIMEOUT) indication. Your main() then does something like: // setup hardware boolean waiting; .... while (FOREVER) { char character; ... start_timer(&waiting, time_limit); if (receive_character(&character, &waiting) == SUCCESS) // do something with "character" else // character didn't show up in the prescribed time limit ... } void start_timer(boolean *flag, timeout_t timeout) { *flag = SET; set_hardware_timer(timeout); } and your ISR does: void ISR(void) { // diddle with the hardware waiting = ~SET; } (I've not proofed this for typos :< ) > Using assembly I would have used "goto" to steer execution where I wanted > it, but how is it approached in C?
From: D Yuniskis on 2 Jun 2010 11:42 Hi Grant, Grant Edwards wrote: > On 2010-06-02, hamilton <hamilton(a)nothere.com> wrote: >> On 6/2/2010 9:12 AM, D Yuniskis wrote: >>> hamilton wrote: >>>>> Using assembly I would have used "goto" to steer execution where I >>>>> wanted >>>>> it, but how is it approached in C? >>>> There is no "goto" in C. >>>> >>>> "goto" in C is bad (very bad) practice. >>> (sigh) No, goto has a role in C. What is wrong is using >>> goto as if it was GOTO (e.g., BASIC). >>> >>> Usually, you can restructure your code with suitable blocks >>> to eliminate the need for a "goto". But, there are times >>> when "goto" is the *right* choice. Convincing yourself >>> that it is "very bad practice" will blind you to when >>> it is the *right* practice! >> As you have stated "you can restructure your code". > > There are times when a goto is simpley the best choice and results in > much simpler and eaiser to maintain code. > >> Why change your code just to use a goto ?? > > No idea what you're asking. Yes. Who said anything about *changing* it to *exploit* a goto! Rather, *avoiding* the goto can be a colossal waste of time and just make the code more brittle. The goal of a HLL (if you want to call C such :> ) is to make it easier to *express* algorithms. Let the compiler sort out how to get what you want out of the code. (and the compiler will often use *lots* of "goto's" -- jumps!) >> Design your code right the first time. ;-) > > Sometimes a goto is the right way. Exactly! -------------^^^^^^^^^^^^^^ I've seen horribly mangled code that went out of its way to avoid using a goto. As a result, it's a nightmare to maintain and very brittle. Not all algorithms lend themselves to simple blocks.
From: D Yuniskis on 2 Jun 2010 11:47 Hi Grant, Grant Edwards wrote: > On 2010-06-02, D Yuniskis <not.going.to.be(a)seen.com> wrote: >> Usually, you can restructure your code with suitable blocks to >> eliminate the need for a "goto". But, there are times when "goto" is >> the *right* choice. Convincing yourself that it is "very bad >> practice" will blind you to when it is the *right* practice! > > I very much agree. > > There are a few places where a goto in a C program is, IMO, the best > choice. Most notably when you need to "signal" an exception that must > cause an exit from inside several nested loops or other control > structures. Adding a bunch of flags to the loops to make them all exit > prematurely due to the exception/failure is convoluted, hard to > maintain, and will cause bugs later in the codes life. Exactly. The "prima donna" syndrome may make you *feel* like you've been extremely clever -- until your code breaks the first time someone tries to modify it (this ignores the costs to *you* to get it working correctly in the first place!) > A simple "goto failure;" is much simpler and easier to read/maintain. > > If you look at C code in things like kernels and network stacks (code > that was written by some very skilled people), you'll see the > occasional goto used to handle what would be an exception in a > language like Python. In C, I often bracket "troublesome" regions in a superfluous "do-while" and "break" from that in lieu of a "goto". But, that assumes you don't *normally* exit that do-while "out the bottom" (i.e., you either stay in it indefinitely *until* one of those "breaks" occurs *or* you "return" out of it) > I've also seen goto used to implement a retry mechanisms where adding > another layer of looping would have been too convoluted.
From: Tim Wescott on 2 Jun 2010 12:55
On 06/02/2010 07:35 AM, hamilton wrote: > On 6/2/2010 6:24 AM, Royston Vasey wrote: -- snip -- >> >> Using assembly I would have used "goto" to steer execution where I wanted >> it, but how is it approached in C? > > There is no "goto" in C. There is a "goto" in C. > "goto" in C is bad (very bad) practice. "goto" in C can be about the worst thing that you can do to a program. But used carefully and sparingly, "goto" in C can provide a very good mechanism for exception handling. I use it occasionally, and it does well for what it is. Were I to see a "goto" in a code review I would pin the author of the code against the wall to make sure he was using it correctly; were I to enter some of my code with a "goto" in a code review I would schedule extra time to be pinned to the wall and justify myself (I'd probably have careful comments in the code, too). Exception handling in object-oriented programming is specifically designed to address the problems in C++ that C's "goto" and long branch (long goto? I can't remember what it's called) solve in C. -- Tim Wescott Control system and signal processing consulting www.wescottdesign.com |