More generally, a breakpoint is a means of acquiring knowledge about a program during its execution. During the interruption, the programmer inspects the test environment (general-purpose registers, memory, logs, files, etc.) to find out whether the program is functioning as expected. In practice, a breakpoint consists of one or more conditions that determine when a program's execution should be interrupted.
Breakpoints were invented for ENIAC, one of the earliest digital computers, by programmer Betty Holberton.[1] In the initial design of ENIAC, program flow was set by plugging cables from one unit to another. To make the program stop at a certain point, a cable was removed, called a breakpoint.[2]
Early mainframe computers, such as the IBM/360, had console switches/dials that allowed breakpoints at specific instruction storage addresses and provided "single cycle" operation, permitting the contents of registers and memory to be observed directly on console lights. The advent of multitasking limited the use of this option since the entire machine was halted.
Programmers have used machine code patches to implement single destructive breakpoints to cause a core dump since the early days of computers. The core dump provided the state of the registers and memory at the exact moment of the deliberate "crash".
The advent of teletypewriter consoles in the 1960s allowed more interactive command line debugging capabilities but it was not until the early 1970s and the arrival of ubiquitous video monitors connected to mainframes that fully interactive, full screen debugging in multitasking environments became a reality. This also permitted step-by-step program execution in a true program animation manner with optional register and memory alterations simultaneously displayed. Initially this type of animation was at the level of disassembled or decompiled machine code, but later advanced to HLL source level animation.
Breakpoints are most commonly used to interrupt a running program immediately before the execution of a programmer-specified instruction. This is often referred to as an instruction breakpoint.
Other kinds of conditions can also be used, such as the reading, writing, or modification of a specific location in an area of memory. This is often referred to as a data breakpoint, or a watchpoint. Many systems also support breakpoints that are only active if a condition is met (such as a variable having a certain value), usually referred to as conditional breakpoint.[3]
When a breakpoint is hit, various tools are used to inspect the state of the program or alter it. Stack trace of each thread may be used to see the chain of function calls that led to the paused instruction. A list of watches allows one to view the values of selected variables and expressions. There may also be tools to show the contents of registers, loaded program modules and other information.
A logpoint is a type of breakpoint that only prints (or "logs") information instead of interrupting execution. Usually the developer can specify a message and/or values of variables to print when execution reaches a specific point.[4]Logpoints are an alternative to putting logging statements into the program being debugged (sometimes called printf debugging), and particularly helpful when changing the program is not practical (for example when debugging an external library called by the program).
Many processors include hardware support for breakpoints (typically instruction and data breakpoints). As an example, the x86 instruction set architecture provides hardware support for breakpoints with its x86 debug registers. Such hardware may include limitations, for example not allowing breakpoints on instructions located in branch delay slots. This kind of limitation is imposed by the microarchitecture of the processor and varies from processor to processor.
Without hardware support (and in multitasking environments), debuggers have to implement breakpoints in software. For instruction breakpoints, this is a comparatively simple task of replacing the instruction at the location of the breakpoint by either:
This technique may be more difficult to implement in multitasking systems using shared program storage (the interrupt may occur on a different thread, requiring resurrection of the original instruction for that thread). Also, if the program resides in protected memory, overwriting of instructions may be prevented.
Some programming language implementations expose their debugging functions for use by other programs.For example, some FORTRAN dialects have an AT statement, which was originally intended to act as an instruction breakpoint.Python implements a debugger accessible from a Python program.[6]These facilities can be and are[7] abused to act like the COMEFROM statement.
You could read Donald Knuth's 1974 paper Structured Programming with go to Statements, in which he discusses various uses of the go to that are structurally desirable. They include the equivalent of break and continue statements (many of the uses of go to in there have been developed into more limited constructs). Is your boss the type to call Knuth a bad programmer?
(The examples given interest me. Typically, break and continue are disliked by people who like one entry and one exit from any piece of code, and that sort of person also frowns on multiple return statements.)
I do not believe they are bad. The idea that they are bad comes from the days of structured programming. It is related to the notion that a function must have a single entry point and a single exit point, i. e. only one return per function.
This makes some sense if your function is long, and if you have multiple nested loops. However, your functions should be short, and you should wrap loops and their bodies into short functions of their own. Generally, forcing a function to have a single exit point can result in very convoluted logic.
If your function is very short, if you have a single loop, or at worst two nested loops, and if the loop body is very short, then it is very clear what a break or a continue does. It is also clear what multiple return statements do.
Most people think it's a bad idea because the behaviour isn't easily predictable. If you're reading through the code and you see while(x < 1000) you assume it's going to run until x >= 1000...But if there are breaks in the middle, then that doesn't hold true, so you can't really trust your looping...
For myself, if I was going to do a loop that broke on more than one condition, I'd do while(x) then toggle X to false when I needed to break out. The final result would be the same, and anyone reading through the code would know to look more closely at things that switched the value of X.
Yes you can [re]write programs without break statements (or returns from the middle of loops, which do the same thing). But you may have to introduce additional variables and/or code duplication both of which typically make the program harder to understand. Pascal (the programming language) was very bad especially for beginner programmers for that reason. Your boss basically wants you to program in Pascal's control structures. If Linus Torvalds were in your shoes, he would probably show your boss the middle finger!
There's a computer science result called the Kosaraju's hierarchy of control structures, which dates back to 1973 and which is mentioned in Knuth's (more) famous paper on gotos from 1974. (This paper of Knuth was already recommended above by David Thornley, by the way.) What S. Rao Kosaraju proved in 1973 is that it's not possible to rewrite all programs that have multi-level breaks of depth n into programs with break depth less than n without introducing extra variables. But let's say that's just a purely theoretical result. (Just add a few extra variables?! Surely you can do that to please your boss...)
What's far more important from a software engineering perspective is a more recent, 1995 paper by Eric S. Roberts titled Loop Exits and Structured Programming: Reopening the Debate ( -1995/LoopExits.pdf). Roberts summarizes several empirical studies conducted by others before him. For example, when a group of CS101-type students were asked to write code for a function implementing a sequential search in an array, the author of the study said the following about those students who used a break/return/goto to exit the from the sequential search loop when the element was found:
Students who attempted to solve the problem without using an explicit return from the for loop fared much less well: only seven of the 42 students attempting this strategy managed to generate correct solutions. That figure represents a success rate of less than 20%.
Yes, you may be more experienced than CS101 students, but without using the break statement (or equivalently return/goto from the middle of loops), eventually you'll write code that while nominally being nicely structured is hairy enough in terms of extra logic variables and code duplication that someone, probably yourself, will put logic bugs in it while trying to follow your boss' coding style.
I'm also gonna say here that Roberts' paper is far more accessible to the average programmer, so a better first read than Knuth's. It's also shorter and covers a narrower topic. You could probably even recommend it to your boss, even if he is the management rather than the CS type.
But, if deep inside you'd find a reason to stop the loop (other than primary-condition), a break or return would have it's use. I'd prefer that over the use of an extra flag that is to be tested in the top-level condition. The break/return is more direct; it better states the intent than setting yet another variable.
The "badness" is dependent on how you use them. I typically use breaks in looping constructs ONLY when it will save me cycles that can't be saved through a refactoring of an algorithm. For instance, cycling through a collection looking for an item with a value in a specific property set to true. If all you need to know is that one of the items had this property set to true, once you achieve that result, a break is good to terminate the loop appropriately.
c80f0f1006