Chapter 1 Imaginary Compiler
Readers can consider what is the error condition of the corresponding program if the compiler can correctly point out all the questions in the code? This does not only mean syntax errors, and any problem in the program, no matter how hidden it. For example, it is assumed that there is a "difference 1" error in the program, and the compiler can use some way to find it and give the following error message.
-> Line 23: While (i <= j)
OFF BY ONE Error: this kind be '<'
Another example, the compiler can find the following error in the algorithm:
-> Line 42: Int Itoa (Int i, char * STR)
Algorithm Error: Itoa Fails When I IS-32768
Again, when the parameter is passed, the compiler can give the following error message:
-> Line 318: Strcopy = Memcpy (Malloc (Length), STR, Length;
Invalid Argument: Memcpy Fails When Malloc Returns NULL
Ok, it seems to be a bit too much to ask the compiler. But if the compiler can really do this, how easy you can imagine whether you write a worse program. That is just a small matter, and the general practical practice of the current programmer is really unable to ratio.
If you use a camera on a spy satellite to align a typical software workshop. I will see that the programmer is in front of the bows and tracks errors on the keyboard; next to the tester, the tester is initiating an internal version of the internal version, and the amount of data that is bombed into the number of people in order to find new mistakes. You will also find that the tester is checking whether the old version of the error slipped into the new version. It can be contemplated that this check-error method is much more effort to check the error with the above imaginary compiler, it is true, and it is a little luck.
luck?
Yes, luck. The tester can find errors, it's not because he notes like a number wrong, a feature does not work or proceed in a desired way? Take a look at the above error given above: Although the program has a "difference 1" error, but if it still works, can the tester can see it? Even if you can see it, then the other two mistakes?
This sounds like it is terrible but tester is to make a large number of input data to the program, and I hope that there is a potential error to be unveiled. "Oh, no! Our testers are not so simple, we also use code coverage tools, automatic test sets, random" monkeys "programs, draw printed or other." Perhaps this, but let's take a look at what these tools do! Overlay analysis tools can specify which parts of the program have not been tested, the tester can use this information to derive a new test case. As for other tools, it is nothing more than "input data, observation results".
Please don't misunderstand, I don't say that the testers are all wrong. I just said that I can use the black box method to fill the data into the program and see what it pops up. This is like determining that a person is not a madman. Ask some questions, get the answer after answering. But still can't determine that this person is a madman. Because we can't know what is thinking in his mind. You will always ask yourself like this: "Is the question I asked? I asked questions ...".
Therefore, do not light on the black box test method. It should also try to imitate the imaginary compiler of the previous, to exclude the influence of luck on the program test, and automatically grasp each opportunity of the wrong.
Consider the language used
When is your last time you look at the advertisement of the sales word handler? If the advertisement is written by the group of Madison Street, it is likely to say this: "Write a note for the children or for the next" Great American Novel ", WordSmasher can do it, Wordsmasher is equipped with a surprisingly 233000 word spelling dictionary, which is more than 51,000 words than the same product. It can easily find the typing error in the sample. Hurry to the dealer to buy a copy. Wordsmasher is from The most revolutionary writing tool since the ball is coming! " The user has continued to promote and influence the market, and they are almost believed in spelling the dictionary, but the fact is not the case. As EM, Abel, and Si, you can find in any of the listing dictionary, but in the case of ME, ABLE and IS, you want to let the spell checker think EM, Abel and Si are also spelling correct Word? If so, then when you see the Suing I wrote, it is very likely that it is unpacked with the wind of the wind. The problem is not that Suing is a true word and is that it is indeed an error here.
Fortunately, some quality spell checked programs allow users to delete the like EM. In this way, the spell check can regard the original legal word as a spelling error. A good compiler should also be able to see such a legal C habits that repeatedly errors to see the errors in the program. For example, such compilers can check the following While loop missed a semicolon:
/ * Memcpy Copy a non-overlapping memory block * /
Void * Memcpy (void * pvto, void * pvfrom, size_t size)
{
BYTE * PBTO = (Byte *) PVTO;
BYTE * PBFROM = (byte *) pvfrom;
WHILE (size -> 0);
* PBTO = * PBFROM ;
Return (PVTO);
}
We can know that the semicolon by the why expressions will definitely be an error since the system's indentation situation, but the compiler thinks this is a full legitimate While statement, its cyclic body is an empty statement. Since sometimes requires empty statements, sometimes no empty statements, so in order to detect unwanted empty statements, compilers often give an optional warning message when encounter empty statements, and automatically warn that you may have the above error. When you are determined when you need a nicker, you will use it. But it is best to use NULL to make it obvious. E.g:
Char * STRCPY (Char * PCHTO, Char * PCHFROM)
{
Char * pchstart = PCHTO;
While (* PCHTO = * PCHFROM )
NULL;
Return (PCHSTART);
}
Since NULL is a legitimate C expression, this program has no interprises. The more advantageous benefits using NULL is that the compiler does not generate any code for the NULL statement, because NULL is just a constant. In this way, the compiler accepts an explicit NULL statement, but automatically uses the implicit empty statement as an error. Only one form of empty statement is allowed in the program, as in order to maintain the consistency of the text, only one of ZERO is used in the text, so I want to remove another plurality of Zeros from the spell dictionary.
Another common problem is unintentional assignment. C is a very flexible language that allows you to use assignment statements anywhere. So if the user is not cautious enough, this excess flexibility will make you mistaken. For example, this common error occurs in the following procedures:
IF (CH = '/ t')
ExpandTab ();
Although it is very clear that the program is to compare the CH and horizontal tab, but in fact, it has become a value of CH. For this program, the compiler must not generate an error because the code is legal C. Some compilers allow users to use simple assignments in the && and | | expressions, and IF, For, and While constructs, it can help users find this error. The basic basis for this approach is that the user is very likely to be equal to the above five cases == accidentally in the assignment number =.
This selection does not hinder the user's assignment, but in order to avoid warning information, the user must take another value, such as zero or empty characters and assignment results to make an explicit comparison. Therefore, for the front STRCPY example, if the loop is written:
While (* PCHTO = * PCHFROM )
NULL;
The compiler will generate a warning message to write;
While (* PCHTO = * PCHFROM )! = '/ 0')
NULL;
There are two benefits. First, modern commercial level compiler does not generate additional code for this redundancy, which can be optimized. Therefore, the compiler that provides this warning selection is trustworthy. Second, it can be risks, although both legal, this is a safer usage.
Another class error can be classified into the "Parameter Error". For example, how many years ago, when I was studying C language, I once called FPUTC:
FPRINTF (stderr, "unable to open file% s. / n", filename);
......
FPUTC (stderr, '/ n');
This program looks like there is no problem, but the parameter of FPUTC is wrong. I don't know why, I always think that the stream pointer (stderr) is always the first parameter of such stream functions. This is not the case, so I often pass many of the information that is useless to these functions. Fortunately, ANSI C provides a function prototype, which can automatically find these errors when compiling.
Since the ANSI C standard requires that each library function must have prototype, the prototype of the FPUTC can be found in the stdio.h header file. The prototype of FPUTC is:
INT FPUTC (INT C, FILE * stream);
If INCLUDE is stdio.h in the program, the compiler will compare each parameter transmitted based on its prototype. If the two types are different, it will generate compilation errors. In the above error example, because the file * type parameters are transmitted at the INT position, the previous FPUTC error can be automatically discovered by the prototype.
ANSI C Although the standard library function must have prototype, the function that the user must be written must also be prototype. Strictly speaking, they can have prototypes or there is no prototype. If the user wants to check out the call error in his own program, you must establish a prototype yourself and keep it consistent with the corresponding function.
I have heard the programmer recently complained that they must maintain the prototype of the function. Especially when you just transfer from the traditional C project to the ANSI C project, this complaint is more. This complaint is of a reason, but if you do not use prototype, you have to rely on traditional test methods to find out call errors in the program. You can ask yourself, which is more important, is it to reduce some maintenance workload, or can I find errors when compile? If you are still not satisfied, consider this fact that you can use prototype to generate a better quality code. This is because: ANSI C standard makes the compiler can be optimized according to the prototype information.
In traditional C, the compiler is basically less about its information for functions that are not currently being compiled. Despite this, the compiler still must generate calls to these functions, and the generated call must work. Compiler implementors to solve this problem is to use standard call conventions. Although this method works, it often means that the compiler must generate additional code to meet the requirements of the invocation. However, if you use the "requires all functions must have prototype" this compiler, the compiler is used to understand the parameters of each function in the program, so you can choose the most efficient call for different functions. Agree. The empty statement, the error assignment, and the prototype check is just a small part of the selection provided by many C compilers, and there are often more other options. The point here is that the user can select whether the compiler warning facility will issue a warning message to the user, and the way it works very similar to the spell checker to process possible spelling errors.
Peter Lynch, is said to be the most good investment company manager in the 1980s. He has said that the difference between investors and gamblers is that investors use every opportunity, no matter how small it is, to fight for interest; Credit is only luck. Users should use this concept as programming activities, select all optional warning facilities of the compiler, and see these measures as a programs that have no risk-free procedures. Don't ask: "Do you have this warning facility? And you should ask:" Why don't you use this warning facility? "To open all warning switches unless there is excellent reason to do this."
All optional warning facilities using compilers
Enhanced prototype
Unfortunately, if the function has the same type of parameters, then the prototype does not detect this call error even if the position of the two parameters is interchanged when calling the function. For example, if the prototype of the function Memchr is:
Void * Memchr (Const Void * PV, INTCH, INT Size);
Then when the function is called, the compiler will not issue a warning message even if it interrupted its character CH and size size parameters. However, if more accurate types are used in the respective interfaces and prototypes, the error check capability provided by the prototype can be enhanced. For example, if you have the following prototype:
Void * Memchr (const void * pv, unsigned char ch, size_t size);
Then, when the function is called, its character CH and size Size parameters are called, and the compiler will give a warning error.
Using more accurate types of defects in prototypes are often the explicit type conversion of parameters to eliminate errors that do not match the type, even if the order of the parameters is correct.
Lint is not so bad
Another check error is more detailed, a more thorough approach is to use LINT, which is hard to expect. Initially, the LINT tool is used to scan the C source file and warn it on the unmiglable code in the source program. But most of the Lint utilities have become more strict, but can not only check the portability issues, but also check those who can be portable and completely syntax but it is probably wrong, those suspicious. Error is this category.
Unfortunately, many programmers still think of Lint as a portable checkup, think it can only give a lot of unrelated warning information. In short, LinT got a reputation that was not worth a trouble. If you think about the programmers like this, then you may rethink your insight. What kind of tool is to be more close to the hypothetical compiler as described above is the compiler you are using, or LINT?
In fact, once the source program is in the form of no LINT error, it is easy to keep this state. As long as you run the LINT for the changed part, it will be incorporated into the original source code after no error. With this approach, don't make too much consideration, as long as you pass one or two, you can write a code without the LINT error. When you reach this level, you can get the benefits of LINT. Use LINT to detect errors missing from the compiler
But I have made modifications are usually
When a technical reviewer with the book, he asked me whether the book intends to include a unit test. I replied: "No". Because although unit tests are related to erroneous code, it actually belongs to another different category, which is how to write a test program for the program.
He said: "No, you misunderstood. I mean whether you intend to point out that programmers should actually perform corresponding unit tests before the new modification is incorporated into the original source code. One of my groups The member is because there is no corresponding unit test after the program has been modified, so that an error enters our original source code. "
This makes me very surprised. Because in Microsoft, most project leaders require programmers to perform corresponding unit tests before merging the source code.
"You didn't ask why he did not do unit test?", I asked.
My friend raised my head from the table to say: "He said that he did not write any new code, but he moved some of the existing code. He said he believes that there is no need to make a unit test."
This kind of thing has happened in my group.
It reminds me of that there is a programmer after making a modification, and even compiling the corresponding code into the original source code. Of course, I found this problem because I generated an error when compiling the original source code. When I asked this programmer how to miss this compilation error, he said: "I have modified usually, I don't have wrong", but he is wrong.
These errors should not enter the original source code, because both can be almost imposed. Why will the programmer make this mistake? It is the ability to estimate our own correct code too high.
Sometimes it seems to skip some steps for designing to avoid the error, but when you take the shortcut, it is the day of trouble. I suspect that there will be many programmers even have to compile the corresponding code, and "complete" a certain feature. I know this is just accidental situation, but the trend of bypassing unit test is stronger, especially as simple changes.
If you find yourself going to bypass a step. And it can be easily used to check the error, then you must stop yourself from bypass. Instead, you should check the error with each of the tools you can. In addition, although unit tests mean erroneous, if you don't make cell testing at all.
If there is a unit test, the unit test is performed.
summary
Which programmer do you know would rather spend time to track tired, not writing new code? I definitely have such a programmer, but I have not seen one yet. For programmers I know, if you promise they no longer use the next error, they will prefer to give up the Chinese dishes in a lifetime.
When you write a program, you must keep in mind the concept of imaginary compiler in your heart, so you can use every opportunity to grasp the error. To consider the error generated by the compiler, the error generated by the LINT and the reason for the unit test failure. Although using these tools should involve a lot of special technologies, how many errors will be in the product?
If you want to quickly and easily find errors, you should use the tool's corresponding characteristics to make an error. The sooner the error, the earlier, the earlier, the earlier, the earlier.
Important:
l The best way to eliminate program errors is as early as possible, as easy as possible to find errors, and seek the smallest automatic error analysis method.
l Strive to reduce the skills required for programmers to check the error. The compiler or Lint warning facility that can be selected does not require the programmer to have any error. In another extreme, the advanced encoding method can be found or decreased, but they also ask the programmer to have more skill, because the programmer must learn these advanced coding methods. Exercise:
1) If you use the compiler selection item that is forbidden to assign a value in the WHILE, why can I find out the calculation priority error in the following code?
While (ch = getchar ()! = EOF)
......
2) Take a look at how you use a compiler to find out an unintentional empty statement and assignment statement. The recommended approach is to make a corresponding choice, causing the compiler to warn information about the following common problems. How can I eliminate these warning information?
a) IF (Flight == 063). The context of the program is a test of 63 flights, but because there is more 0 to make 063 into an eight-input number. The result became a test of 51 flights.
b) IF (PB! = NULL & PB! = 0xFF). Here you don't care about the && type to be &, even if PB is equal to NULL, * PB! = 0xFF will be executed.
c) quot = numer / * pdenom. There is unintentionally unintentional result, so that / * is interpreted as the beginning of the comment.
d) Word = BHIGH << 8 blow. This statement is interpreted as: word = BHIGH << (8 blow)
3) How can the compiler can give a warning for the "ELSE" without pairing ELSE "? How do users eliminate this warning?
4) Look at the following code:
IF (CH == '/ t')
ExpandTab ();
In addition to the way to use simple assignment in the IF statement, another well-known way to detect this error is to reverse the operations of the assignment number on both sides:
IF ('/ t' == CH)
ExpandTab ();
This should be == when you should type ==, the compiler will report an error because it is not allowed to assign a constant. Is this approach thoroughly? Why is it as high as compiled program switching? Why is the new programmer replaced the number of assignments?
5) The preprocessor program may also cause certain unexpected results. For example, macro uint_max is defined in limit.h, but if you forget the Include's header file in the program, the following directives will be silent, because the pre-delegation will replace the predefined uint_max to 0:
......
#if uint_max> 65535u
......
#ENDIF
How to make the pre-processing program report this error?
Question:
In order to mitigate the workload of the maintenance prototype, some compiler will automatically generate prototypes for the compiled program at compile. If you use the compiler that does not provide this option, write a usage program to complete this work. Why is the standard coding agreement makes the writing of this usage are relatively easy?
Question:
If your compiler does not support the warning facilities mentioned in this chapter (including exercises), promote the corresponding manufacturer to support these facilities, and we must urge them to allow users to set or cancel the inspection of certain types of errors. In addition, it is necessary to selectively set or cancel some specific warning facilities. Why do you do this?