C ++ programming error

Source: Internet
Author: User

C/C ++ has many errors that are easy for beginners (or even experienced programmers. Understanding such errors can free you from falling into it.
Forgot the initialization pointer
This error is only a special form of "forgetting to initialize variables" error (variables in C/C ++ are not automatically initialized, while basic can ). The cause of this error is that the consequences are often worse:
Void somefunction ()
{
Int * pnvar
Int nval;
Nval = * pnvar; // bad enough.
* Pnvar = nval; // much worse.
}
In this example, the pointer variable pnvar is never assigned a value. Therefore, you must assume that it contains messy data and read poorly from a chaotic information pointer, because the result must be messy data and it is worse to write data to a chaotic information pointer, because it will cause some unknown data to be overwritten.
If the area to be overwritten is useless, this is no harm. If the region to be overwritten is useful, data will be lost. This type of error is so difficult to find because Program The problem is thrown only when you attempt to use the lost data. This problem may occur long after the data is lost.
The manual judgment of this problem is very difficult, and the visual C ++ compiler has made some efforts to avoid it. For example, a warning is generated when you compile the above functions. In this case, the compiler will tell you that the variable is not assigned a value before use. In many cases, it is impossible to tell you.
The Windows 95 operating system tries to protect the memory to solve the problem to some extent: if an application attempts to read or write data from its memory, Windows can usually intercept the request, and immediately terminate the program. Unfortunately, Windows 95 cannot intercept invalid access to the memory owned by the application, nor can it intercept all illegal access, because some gaps must be retained and Open in the name of compatibility with Windows 3.1.
Forgot to release heap memory
Remember that any memory allocated from the heap must be released. If you forget to release the memory, the system memory will become smaller and smaller until your program cannot run and crash.
This problem may occur in the following situations:
Car * getanewcar (INT noccupants)
{
Car * pcar;
If (noccupants <4)
{
Pcar = new car (2); // get a two-door.
}
Else
{
Pcar = new car (4); // otherwise, a four-door.
}
Return pcar;
}
Void gotothestore (INT noccupants)
{
// Get a car.
Car * pcar = getanewcar (noccupants );
// Now drive to the store.
If (pcar)
{
Pcar-> drive (store );
}
}
In this example, the function gotothestore () first allocates a new car to drive-this is a waste of time, but you will certainly agree Algorithm It works normally. As long as a new car is assigned, it will drive to a store that calls pcar-> drive (store) to which it points.
The problem is that after it securely reaches the destination, the function does not destroy the car object. It simply exits, causing memory loss.
Generally, when the object pcar has a scope in the program, the programmer should rely on the Destructor ~ The car releases the memory. But I cannot do it here, because the pcar type is not a car but a car *. When the pcar is out of scope, the Destructor will not be called.
The corrected function is as follows:
Void gotothestore (INT noccupants)
{
// Get a car.
Car * pcar = getanewcar (noccupants );
// Now drive to the store.
If (pcar)
{
Pcar-> drive (store );
}
// Now Delete the object, returning the memory.
Delete pcar;
}
All objects constructed using the new operator should be deleted using the delete operator, which must be kept in mind.
Returns a reference to the local memory.
Another common memory-related problem is that the address of the local memory object is returned from the function. The object is no longer valid when the function returns. When a function is called next time, the memory address may be used by the new function. If you continue using this memory pointer, it is possible to write the local memory of the new function.
This common problem occurs in this way:
Car * getanewcar (INT noccupants)
{
Car * pcar;
If (noccupants <4)
{
Pcar = & car (2); // get a two-door.
}
Else
{
Pcar = & car (4); // otherwise, a four-door.
}
Return pcar;
}
Note how the pointer pcar is assigned a local address of an untitled object created by the constructor car. No problem so far. However, once the function returns this address, the problem arises because the temporary object in the closed braces will be destructed.
Confusing Operators
C ++ inherits a set of operators with chaotic meanings from its predecessor C. Coupled with the flexibility of syntax rules, it makes it easy to cause confusion to programmers, so that programmers can use incorrect operators.
The most famous example of this situation is as follows:
If (nval = 0)
{
// Do something if nval is nonzero.
}
The programmer apparently wants to write if (nval = 0 ). Unfortunately, the preceding statement is completely legal. Although it does not make sense, the C ++ statement assigns the nval value to 0 and then checks the result to see if it is non-zero (this is impossible ). The result is enclosed in braces. Code It will never be executed.
Other pairs of operators that are easy to make mistakes are & and &, And/AND //.
Four faces of 0
According to the method in which it is used, constant 0 has four possible meanings:
☆Integer 0
☆It cannot be the address of the object address
☆Logical false
☆String Terminator
I can prove to you that the differences between these meanings are very practical. For example, the following values are valid:
Int * pint;
Pint = 0; // This is leagal.
The following values are invalid:
Int * pint;
Pint = 1; // This is not.
The first value assignment is valid because the second definition in the table: constant 0 can be an address, but constant 1 cannot.
The multiple performances of this meaning cause some difficult-to-find errors:
// Copy a string from psource to pTARGET -- incorrect version.
While (psource)
{
* PTARGET ++ = * psource ++;
}
In this example, the while loop tries to copy the source string pointed by psource to the memory block pointed by pTARGET. However, unfortunately, the condition is wrong. It should be written as follows:
// Copy a string from psource to pTARGET -- incorrect version.
While (* psource)
{
* PTARGET ++ = * psource ++;
}
You can see that when psource points to a null character, the termination condition appears. This is the fourth definition of 0. However, the code written here is to check whether the address psource is zero. This is the second definition.
The final result is that the while () loop continues to write into the memory until the program crashes.
Other definitions of 0 may also lead to confusion. The only solution is to be careful when using constant 0.
Confusion
The compound statement is very confusing, but C ++ -- maintains reverse compatibility with C with its enthusiasm -- but it also produces conflicts between statements. You must avoid such conflicts.
Class myclass
{
Public:
Myclass (INT narg1 = 0, int narg2 = 0 );
};
Myclass MCA (1, 2 );
Myclass MCB (1 );
Myclass MCC ();
MCA is an object composed of parameters 1 and 2, while MCB is an object composed of parameters 1 and 0. Therefore, you may think that MCC is an object composed of parameters 0 and 0, but this is not the case. MCC () is a function without parameters. It returns the object of the class myclass with numerical values.
Another confusion arises from the use of the initialization operator =:
Myclass MCB = Na; // same as myclass MCB (NA)
To enhance compatibility with C, allow =; however, you should avoid this structure because it is not always applicable. For example, the following program will not have the expected effect:
Myclass MCA = Na, NB;
This indicates an object MCA (NA), followed by an independent object Nb using the default constructor, rather than an object MCA (Na, Nb ).
Stick to the C ++ format-this is the safest.
Chaotic computing order
The order of the C and C ++ operators allows you to know how to calculate the following expressions:
A = B * C + D;
However, the order of order does not affect the order of subexpressions. Let's change the expression of the example in a seemingly unimportant way:
A = B () * C () + d ();
The question is, in what order does this expression call functions B (), C (), and D ()? The answer is that the order is completely unknown. Worse, the sequence cannot be determined by the use of parentheses. Therefore, the following expressions do not work:
A = (B () * C () + d ();
The order of function compute is generally not worth noting. However, if these functions have side effects and affect each other in some way (called side effects), order is important. For example, if these functions change the same global variables, the results are different, depending on the order in which the functions are called.
Even when function calls are not involved, their side effects may also be affected:
Int ni = 0;
Cout <"Na [0] =" <the problem with this expression is that a single expression contains two subexpressions with mutual side effects-the variable Ni is incremental. Which na [Ni ++] is executed first, the NA [Ni ++] on the left or the NA [Ni ++] on the right? The above code may work as expected, but it may not.
Virtual member functions
To overload a virtual member function in a subclass, the parameters and return types of the function in the subclass must be described in the same way as those in the basic class. This is not always clear. For example, the following code seems to make sense:
Class base
{
Public:
Virtual void afunc (base * pb );
};
Class subclass: public Base
{
Public:
Virtual void afunc (subclass * PS );
};
This code will be compiled, but it will not be later compiled. The parameters of the function base: afunc () are of the base * type, while those of the subclass: afunc () function are of the subclass * type. They are different.
The only exception to this rule is the following example, which complies with the ansi c ++ standard:
Class base
{
Public:
Virtual void base * afunc ();
};
Class subclass: public Base
{
Public:
Virtual void subclass * afunc ();
};
In this example, each function returns the address of its inherent type object. This technology is widely used, so the Standards Committee decided to acknowledge it.
Call a virtual member function from the constructor
Calling a virtual function from the constructor is a preliminary compilation. In this way, it short-circuited the concise capabilities that may have been possible:
Class base
{
Public:
Base ();
Virtual void buildsection ();
};
Class subclass: public Base
{
Public:
Subclass ();
Virtual void buildsection ();
};
Base: Base ()
{
Buildsection ();
};
In this example, the programmer wants the constructor to call buildsection () polymorphism. When the constructed object is a base object, base: buildsection () is called (), when the object is a subclass object, call subclass: buildsection ().
This example does not work because of the following simple reasons: When buildsection () is called, the object being constructed is only a base object. Even if the object eventually becomes a subclass object, wait until the subclass constructor passes it over again. In these cases, calling subclass: buildsection () may be fatal. Even if the object will eventually become a subclass object, but when calling buildsection (), the object is only a base object, and this call must be first combined with the function base :: buildsection ().
Pointer aligning
When you execute your program on an 80x86 processor (such as your PC chip), this problem is not fatal, but for most other chips, this is fatal. It also affects the ability of your applications to be migrated to another environment. In addition, even for Intel processors, this problem will lead to lower performance than the standard.
When your pointer is converted from one type to another, a non-aligned pointer may be generated ). The processor generally requires that the address of the memory block be aligned with a boundary that matches the size of the memory block. For example, a word can only be accessed on the word boundary (the address is a multiple of two), a double word can only be accessed on the word boundary (the address is a multiple of four), and so on.
The compiler usually ensures that this rule is monitored. However, when your pointer type is converted from one type to a larger type, you can easily violate this rule:
Char CA;
Char * Pc = & Ca;
Int * PI;
Pi = (int *) PC;
* Pi = 0; // This may be fatal.
Because the character is only one byte long, the address & CA may have any value, including odd values. However, PI should only contain an address multiple of four. Through conversion, the PC can be assigned to Pi, but if the address is not a multiple of four, the next value assignment may cause the program to crash.
For Intel processors, this assignment is not fatal even when the Pc value is an odd number. Although it takes a long time, the assignment can still be executed normally. Please guard against non-aligned pointers.
This occurs only when you are converting your pointer from pointing to a type to pointing to a large type.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.