📜 ⬆️ ⬇️

The forbidden fruit of GOTO is sweet (version for microcontrollers)!

Good day!

What is your attitude to the goto operator in C / C ++ languages? Most likely, when you learned to program, you used it. Then you learned that it was bad, and you forgot about it. Although sometimes with complex error handling ... no, no, there try ... throw ... catch. Or to exit nested loops ... no, there are flags and a lot of difficulties. Or when nested switches ... no, no, no, there the same flags.
And yet, sometimes in the stillness of the night you would allow a sinful thought into your subconscious mind - “why not use the goto here ? And the program seems to be slimmer, and optimally out. Yes, it would be good ... But no - it is impossible , they forgot! ”.
Why is it so?
Under the cut - a small investigation and mine, based on years of practice and different platforms, attitude to this issue. This article is an analogue of the same for C ++ , but here moments for C and for microcontrollers are highlighted.

Request to goto's ardent opponents - do not overthrow me into hell fire minus karma just because I raised this topic and I am a partial supporter of goto!

A small historical excursion


Those who, even without me, know perfectly well what a combinational circuit is, a circuit with memory, and how an assembler grew out of it - you can safely jump further - to a conclusion.

It all started with combinational circuits



At the beginning there was a word - and the word was a function. It is not so important that it was a Boolean function of a logical variable - then, in this basis, they managed to implement all (almost) mathematics, and then texts, graphics ... Anyway, it turned out that with the help of computer technology it is very convenient to do arithmetic, and then trigonometric and other actions and find the values ​​of the functions of the variable.
')
In other words, you had to make a device that, by the value of a variable (variables), found the value of a function.

To solve this difficult task, a sequential algorithm was built to perform arithmetic operations (in the case of a given accuracy of calculations in such an algorithm, each arithmetic operation can be performed in one clock).

Having an algorithm, it is easy to construct a combinational circuit - a circuit that instantly (up to the response of logic devices and signal propagation time) gave the answer at the output.
The question is - do you need any transitions? No, they are simply not there. There is a consistent course of action. All these actions can be implemented ultimately in a single cycle (I don’t argue, it will be very, very cumbersome, but given the digit capacity of all the data, any student will build such a scheme for you - and even more so a synthesizer for VHDL or Verilog).

But then the memory circuits intervened.



And then someone's clever head came up with a scheme with feedback - for example, an RS trigger. And then the state of the scheme appeared. And the state is nothing but the current value of all the elements with memory.

The appearance of such memory elements made it possible to make a revolutionary leap forward from hard-coded devices to firmware automata. Simply put, there is a command memory in firmware automata. There is a separate device that implements the current firmware (addition, subtraction, or something else). But the choice of "current" firmware is a separate device - let it be a "sampling device".

The question is - are there any transitions? Definitely yes! Moreover, there are unconditional transitions (the address of the next command does not depend on the current state of the data) and conditional (the address of the next command depends on the state of the data).

Is it possible to do without them? No way! If we don’t use transitions, we’ll go back to the memoryless combinational circuit.

As a result, we come to the assembler


The apotheosis of such computing devices are micro, simple and super computers. All of them are based on a language of codes, which is rather easily converted into an Assembler with an approximately identical set of instructions. Consider a certain averaged assembler of microcontrollers (I am familiar with the assembler for ATmeg-i, PIC and AT90). How does his transition work?

Transitions are unconditional (just go to the next address, go to the subroutine, exit it) and conditional (depending on the state of the flags).

I declare with all responsibility - it is impossible to do without transition operations in assembler! Any program in assembly language is just full of them! However, there is no one to argue with me here, I think it will not.

Total


What can be summed up? At the microprocessor level, transition operations are used very actively. It’s almost impossible to write a real program that doesn’t use them (maybe it can be done, but it will be a super-mega perversion and certainly not a real program!). Nobody will argue with this either.

But why, then, in languages ​​of a higher level - let's concentrate on C for microcontrollers - did the goto operator suddenly fall into disfavor? ..

Little about algorithms




And now we will look at the cunning algorithm. I have no idea what this nonsense is, but it must be implemented. We assume this is the TK.

Here A, B, C, D, E are some operations , not a function call! It is possible that they use a lot of local variables. And it is quite possible that they change their state. That is, in this case we are not talking about calling functions - some actions will not be detailed.

Here’s what it looks like with goto:

if (a) { A; goto L3; } L1: if (b) { L2: B; L3: C; goto L1; } else if (!c) { D; goto L2; } E; 


Very concise and readable. But - no! Let's try without goto:

 char bf1, bf2, bf3; if (a) { A; bf1 = 1; } else bf1 = 0; bf2 = 0; do { do { if (bf3 || b) bf3 = 1; else bf3 = 0; if (bf3 || bf2) B; if (bf3 || bf1 || bf2) { C; bf1 = 0; bf2 = 1; } if (!bf3) { if (!c) { D; bf3 = 1; } else { bf3 = 0; bf2 = 0; } } } while (bf3); } while (bf2); E; 


Did you understand anything from the logic of the second listing? ..
Compare both listings:



But in the second listing there is no goto!

I was also offered this algorithm to implement something like this:


if a
A
C

while b or not c
if not b
D
B
C

E



It seems to be beautiful, yes? Why not make a copy-past, why not wind up additional checks, why ... do anything except goto !!!

Well, okay, in life such algorithms almost never occur. Better talk about life.

goto in real programs



In my more than 20 years of experience, I went through several hardware platforms and a dozen programming languages, participated in writing a large ActiveHDL software product, made a commercial database and many small programs for debugging equipment used in the Olympics, and also made devices for this Olympiad itself (already several Olympiads, to be exact). In short, I am fiddling with programming. And, yes, I forgot - I graduated with an honorary diploma from KNURE - I mean, in theory, I am also a secular.

Therefore, my subsequent reflections and situations ... let's say, I have a moral right to them.

Implicit use of goto


In C, there are many operators that are actually banal goto - conditional or unconditional. These are all kinds of for (...), while (...) {...}, do {...} while (...) loops. This is an analysis of numeric variables switch (...) {case ... case ...}. These are the same interrupt / branch statements in the break and continue cycles. In the end, these are calls of funct () functions and exiting return.

These goto are considered "legal" - what is the goto itself illegal?

What goto is accused of


He is accused of the fact that the code becomes unreadable, poorly optimized and errors may occur. This is about practical cons. And the theoretical - it's just bad and illiterate, and that's it!

As for unreadable code and poor optimizability, take another look at the listings above.
As for the probability of errors, I agree, such code is perceived somewhat more complicated due to the fact that we are used to reading the listing from top to bottom. But that's all! And what, other means With safe and cannot create errors in the code? Yes, take at least type conversions, work with pointers. There to screw up - for nothing to do!

Do not you think that a knife is a very dangerous thing? But for some reason in the kitchen we use it. And 220 volts - how terrible it is! But if you use it wisely, you can live.

Same goto. You need to use it wisely - and then the code will work correctly.

And about the theoretical arguments - forgive me, this is a dispute about tastes. Do you use the Hungarian notation ? I - no, I can not stand her! But I'm not saying that she is bad because of this! Personally, I think that a variable should carry a meaning - for which it was created. But I will not prohibit other people from using this method of naming!

Or there are aesthetes who believe that writing a = ++ i is illiterate, it is necessary to write i = i + 1; a = i. And now, ban it too?

Error processing


Take the processing of input packets from some external device:

 pack = receive_byte (); switch (pack) { case 'A': for (f = 0; f < 10; ++f) { … if (timeout) goto Leave; … } break; case 'B': … } Leave: … 


We received a package header. Analyzed Yeah, the package 'A' means we have to do something 10 times. We do not forget to control the time of work of this area - and suddenly the second side is stuck? Yeah, it’s still hanging - the timeout condition worked - then we’re going out - out of the loop, out of the switch.

The same can be done with all sorts of flags - but it will work slower, plus take up such a deficient memory cell. It's worth it?

This case is simple. But receive_byte () can also be a macro function with timeout processing. And there will also be such sharp exits here.

This is exactly the case where I actively use goto. This allowed me not to fall into a “freeze” in case of problems with external devices, UART, USB, etc.

Exit nested loop out


See the program below:

 char a, b, c; for (a = 0; a < 10; ++a) { for (b = 0; b < a; ++b) { if (!c) goto Leave; } for (b = 10; b < 15; ++b) { d (); } } Leave: e (); 


What is happening - you understand? There is a nested loop. If any condition comes, we leave all subsequent processing.

This code with flags looks different:

 char a, b, c, f1; f1 = 1; for (a = 0; a < 10 && f1; ++a) { for (b = 0; b < a && f1; ++b) { if (!c) f1 = 0; } if (f1) { for (b = 10; b < 15; ++b) { d (); } } } e (); 


What happened in this case? At each iteration, we now check the flag. Do not forget to check it further. These are trifles, if there are few iterations and it is about the "dimensionless" memory of the PC. And when the program is written for the microcontroller - all this is already becoming significant.

By the way, in connection with this, in some languages ​​(if I’m not mistaken, in Java) there is an opportunity to break out of the loop by a label like break Leave. The same goto, by the way!

I can give the exact same example with processing in switch (...) {case ...}. I often come across this when processing incoming packets of unequal structure.

Automatic code generation


Do you know machine programming? Or any other automated code generation? Say, the creators of lexical handlers (without using the cumbersome boost :: spirit). All these programs create code that can be used as a “black box” - it doesn't matter to you what's inside; It matters to you what he does. And inside there goto is used very, very often ...

Exit in one place


On C, sometimes you have to write something like:

 int f (…) { … if (a) { c = 15; return 10; } … if (b) { c = 15; return 10; } …  = 10; return 5; } 


This code will look much more accurate like this:

 int f (…) { … if (a) goto Exit; … if (b) goto Exit; …  = 10; return 5; Exit: c = 15; return 10; } 


Is the idea clear? Sometimes you need to do something at the exit. Sometimes a lot of things have to be done. And then goto helps a lot. I also have such examples.
It seems to have listed everything, now you can let it down ...

Total



This is my point of view! And she is fair to me. Maybe - for you, but I will not force you to follow her!

So, for me it is obvious that goto helps to solve some problems more optimally and with better quality.
And it happens the other way around - goto can cause a lot of problems.

UPD : Having read a lot of comments, I selected for myself the positive aspects of using goto and the negative ones.

Advantages of using goto :

Cons of using goto :


Who else will tell the pros / cons? I will enter if they are justified.

Once again I pay attention: I do not call Tuli goto everywhere ! BUT in some cases, it allows you to implement the algorithm much more effectively than all other means.

Source: https://habr.com/ru/post/114270/


All Articles