Some views on functions and character pointers.

zhaozj2021-02-16  79

Some beginners seem to have some confused to call the function of the pointer, I am presenting two sentences. I hope some help for beginners, and I also ask the expert to finger. Learn each other and make progress together. First, please see the small program below: * / # include using namespace std; char * mystrcpy (char * p); int main () {char * p = null; cout << mystrcpy (p) << endl; // This sentence does not prevent the STR output for the convenience of explanation. Cout << p << endl; // This sentence does not prevent the call to P output. Return 0;} char * p) {char STR [15]; STRCPY (STR, "I LVOE C !"); P = Str; Return Str;} // Is this program main STR output and Can the P output statement get the correct result? If the mystrcpy function is changed to several of the following, what is the result? A: char * mYStrcpy (char * p) {char * str = new char [15]; // This has been modified. STRCPY (STR, "I LVOE C !"); P = Str; Return Str;} / b: char * mystrcpy (char * p) {static char STR [15]; // This has been modified. STRCPY (STR, "I LVOE C !"); P = Str; Return Str;} /// c: char * mystrcpy (char * & p) // This has been modified. Also modify the declaration of the function at the program head accordingly. {Char * str = "i lvoe c !"; // has been modified here. // STRCPY (STR, "I LVOE C !"); // This has been modified here, Return Str;}, let's take a look, let us first look at the original: char * mystrcpy (char * P ) {Char STR [15]; STRCPY (STR, "I LVOE C !"); P = Str; Return Str;} We know that Str is a local variable, returning a local address is not good, but P output line Don't you? Because P is declared in main? Should you do it? ! The answer is not. Because Str is allocated in the stack area, the stack memory is retracted after the function is running (but does not deposit it, but it should not be accessed, otherwise it is considered illegal access, the result is "undefined ", What is not defined? It means that its behavior is uncertain, and there is possible to happen. For example, format your hard drive, the system crashes, grace, give you the same-sex BOSS to ask for love emails.), So The STR output is resended to the memory is illegal, and it should be refused to write a function that returns a local address. P = STR, P value is also a partial address, so it is not.

Take a look at A: A: char * mystrcpy (char * p) {char * str = new char [15]; // This has been modified. STRCPY (STR, "I LVOE C !"); P = Str; Return Str;} STR output can be obtained correctly, and P output is still not. Because Str is the memory allocated in the heap, the memory allocated in the heap is always occupied, until the end of the main function. So it will be released in time when it is no longer used. Here, the function returns the memory address in the heap, so when the COUT << MyStrcpy (P) << ENDL statement is, the output is a string stored in the heap. You can get the correct answer. However, there is a heap memory release problem, and should remember to release it in the function of calling the function. But when the myscpy function and the main function is not written by the same person, the memory leaks will eventually cause the system. Maybe you will say: Cut, isn't it 15 bytes, what is going on, now there is a very low memory. To know that the function may be transferred multiple times, such as for (int i = 0; i <100000000; i ) {for (int J = 0; j <100000000; J ) {char * p = null; char * str = MySTRCPY (P); // do something ...}} and the program may be a few years,.. So it is best to avoid writing such a function, it is best to assign a stack in a function, and then release the stack memory before the end of the function. If you really need to allocate a stack memory in a function, you should do some protection work. For example: Class Object; // This is a large class defined elsewhere. Std :: auto_ptr func () {return std :: auto_ptr (new object);} The return type of the function is: Auto_Ptr , using the C standard library template Auto_Ptr , You can use this in the call function: std :: auto_prt apobjet = func (); then you can call members in the Object object via the apobject pointer. It automatically releases the memory in the heap when the life of the apobject is ended. But the auto_ptr is limited, there are some restrictions. For details, please refer to the C standard library, and the C Primer book is also introduced in related knowledge. If the new [] in the func function is new [], the return value of the function should write a template class to package it, call Delete in the template class []. Back to our topic, P output is also No way. Let's take a look at the essence of function calls. There are three parameters of the function of the function in C , there are three ways to pass: pass values, the address (actually transmitted value), and the pass reference (there is no in C).

When a function is called, the memory is allocated in the stack space, first assign some memory to store the address returned to the main modifier, then allocate the memory, then put the arguments COPY, this Execution of code that is called a function is encountered when encountered a return result; statement, a temporary object will be generated at the modified value of the function, and then the result of the result is passed (so please note that the return is not the result itself, but A copy of the Result, or the address of the object), then collects the stack space memory, and finally continues to run in the main adjustment function. Let's look at an example: I want to write a function whose function is to exchange two integers, and then return them and. INT myswap (int val1, int avl2) {int result = VAL1; VAL1 = VAL2; VAL2 = VAL1; RESULT = VAL1 VAL2; RETURN Result;} int main () {Int a = 3; int b = 5; int sun = Myswap (a, b); cout << "a =" << a << "/ tb =" << b << "/ ta b =" << Sun << Endl; Return 0;} When the myswap function is mobilized, first allocate memory in the stack, store the address returns to the address. The memory space of the variable VAL1 and VAL2 is then assigned, and the values ​​of A and B are then copy to VAL1 and VAL2, respectively. Then, the memory space of TEMP is started and initialized (so four memory is allocated in the stack space belonging to the called function, and each of the specific data types and platforms, these should be all in the Win32 platform. It is 4 bytes.). Then swap the value of VAL1 and VAL2, and then seek, when it encounters return result;, a temporary object TEMP is generated at the return of the function, and then the value of the result is copy to TEMP. After the COPY is completed, the stack area is retracted, and then executes Sun = Temp; After the statement ends, the life of TEMP is over. We see that the values ​​of Val1 and VAL2 are exchanged throughout the process, and the values ​​of A and B have not exchanged. Or the original value. This is the situation of the value, and the entire process has several times. If the two objects participating in the exchange are not integrated, the COPY constructor and the destructive function must be mobilized multiple times, and there are many overhead. Therefore, the pass value is a low efficiency.

Do the following modification: int * myswap (int * VAL1, INT * AVL2) {int result = * VAL1; * VAL1 = * VAL2; * VAL2 = * VAL1; Result = * VAL1 * VAL2; RETURN & RESULT;} int Main ) {INT A = 3; INT b = 5; int * sun = myswap (& A, & b); cout << "a =" << a << "/ tb =" << b << "/ ta b = "<< * Sun << endl; return 0;} When the function is mobilized, the memory, pointer variable VAL1, VAL2, which is used to save the returned address is sequentially allocated. Then, respectively, the address COPY of A, B is then assigned a space of the result, followed by the extracting the address in the VAL1 memory space, and then assigns the number of the address (ie: A) to the Result, then exchange A, B value, then, finally, finally put the result's address COPY to TEMP (at this time Temp is a pointer), then collect the stack space, then assign the TEMP content to Sun, Sun points to the stack that has been recovered. One memory, cout << * sun << Endl; belongs to illegal access. So the program should be changed: int result; // global variable. Assign memory space in the global data area. INT * myswap (int * VAL1, INT * AVL2) {result = * VAL1; * VAL1 = * VAL2; * VAL2 = * VAL1; Result = * VAL1 * VAL2; RETURN & RESULT;} This encountered Return & Result , Put the address of the memory of the overall variable RESULT in the global data area to TEMP, then collect the stack space, (cannot recover the result space, because it is not in the allocated memory), then assigning the contents of TEMP to Sun, Such Sun's content is a period of memory in the global data area. Everything is OK. This is an addressing method.

Finally, look at the delivery reference, change the program to: int & mal1, int & avl2 {int result = VAL1; VAL1 = VAL2; VAL2 = VAL1; Result = VAL1 VAL2; RETURN RESULT;} int main () {INT A = 3; int b = 5; int & sun = myswap (a, b); cout << "a =" << a << "/ tb =" << B << "/ ta b =" << Sun << Endl; Return 0;} When the function is called, the stack space is allocated: return the address, reference VAL1, and the memory space of the reference VAL2, the size and pointer is the same. Then, the address COPY of A, B is then assigned a subsultative space, and the content is taken out when VAL1 is used (which is the address of A.) Remove the number of A, which gives it to Result, which is almost the same below. . There is also a problem that references to returning local variables. Change to: int result; // Global Variable INT & MYSWAP (INT & VAL1, INT & AVL2) {Result = VAL1; VAL1 = VAL2; VAL2 = VAL1; Result = VAL1 VAL2; RETURN RESULT;} When encountering returnrate; Give the address of the global variable RESULT to TEMP, collect the stack space, give the TEMP to Sun, the Sun's content is the address of the global variable Resul, only the compiler knows that he is a reference, after all of its operations Is the number of ports stored in the address saved in its memory. We have seen that the reference transfer is achieved by a pointer, but his operation is the same as the pass value. The killing power of the pointer is too strong. If you just reference the object, please do not use the pointer with reference. Here is another trick about the value.

When the return value is a custom object, we'd better use the reference to return instead of the value returned, but sometimes our program requires returns an object, and when the return reference is not suitable, the following cases are compared: Class Object; Object Func (Object & a, Object & B) {// do something object Sun (A B);} should be written: Object Func (Object & A, Object & B) {Return Object (A B); What is the difference between the two? The former first creates a Sun object and initializes, then create a TEMP object, then call the COPY constructor to copy the Sun to Temp. The latter directly establishes the TEMP object, and uses (A B) to initialize Temp. That is, the former is equivalent to: Object Sun (A B); Object Temp = Sun;, the latter is equivalent to: TEMP (A B); two steps of constructing Sun and sectors. Corresponding, we should also write int RES (x1 x2); RETURN RES; directly write: return Int (x1 x2); return to our topic, why not do the P output of A situation? Char * mystrcpy (char * p) {char * str = new char [15]; // This has been modified. STRCPY (STR, "I LVOE C !"); P = Str; Return Str;} Function Char * MyStrCPY (CHAR * P) When the stack space is allocated: return the address, the space of the Conversion P, then put the real The content of the reference (ie: null, note, the content of P is transmitted, not the address of the P itself), then assigns the space of the STR pointer, then assigns a 15-byte space in the heap, STR points to the stack space The first address, then call the STRCPY function, so that the contents in the heap are "I Love C !", And then the STR is assigned to P, at this time, the meticulum P points to the heap space. Then return to the STR, then collect the stack space, STR and the shape of P, are not. Everyone saw that in addition to giving the value null copy of the real parameters Null Copy, there is no relationship again. The P or NULL in MAIN after the function call. To think that the parameters of the actors can change the parameters of the function to the pointer to the pointer: char * mystrcpy (char ** p) // This has been modified, please modify the function of the function accordingly. {Char * str = new char [15]; // This has been modified.

STRCPY (STR, "I LVOE C !"); * p = Str; // has been modified here. Return Str;} and change the call in the MYSTRCPY in the main function to: MySTRCPY (& P), so that the PO output can be obtained correctly. Next, look at B: b: char * mystrcpy (char * p) {static char STR [15]; // This has been modified. STRCPY (STR, "I LVOE C !"); P = Str; Return Str;} B, STR output can be obtained correctly. P Output is not available (why the same A situation). We know that when a program is executed, the memory space is divided into four districts: Code Area: Store my program code. Global data: global variables, static data, constants allocate memory in this area. Stack Area: The local variable, function parameters, return data, return address, and the like allocated to the run function are stored in the stack area. Heap Area: The area allocated by New, New [], Malloc and other functions. Among them, in particular, the memory needs to be particularly concerned. If the assigned memory is not used to call DELETE, DELETE [], free and other functions to be released, and should pay attention to match, the memory allocated by the new, you should call Delete. Release, you cannot call DELETE [] to be released. And you can only release it once. For delete p; if P is a pointer to the custom type, the false code it implemented is as follows: if (p == null) {return;} // Call the destructor of the object points to the object to the object; P-> Destruction (); free (p); we see, for a value of NULL P, regardless of how the delete P is not a problem, but for a pointer P, a pointer P, continuous call two delete p error. Because when the first call, the destructor is first called, then release the heap memory. Just simply release, but did not clear the content in the memory, just saying that when other programs need to be a pile of memory, you can use this released memory, and do not change the contents of P to zero, The address in P does not change, or points to the pile. Some beginners think delete p; that is, the P 's life is over. Not like this. For the following code: Class Object; Object * g_pObject = new object; void func () {// with g_pobject do something ... delete g_pobject;} first assign 4 bytes of space to variable g_pobject, Then, in the stack area, the SIZEOF (Object) size memory is then invoked, then the constructor of the Object is called, and an Object object is constructed, and then the first address COPY of the heap space is stored in the memory space of the global variable g_pobject. When the FUNC function is called, delete g_pobject; means that the content stored in the four byte memory space allocated by G_PObject, look at the one where it point to the pile, then put the one The acres is three minutes. The entire process g_pobject is still the original g_pobject; it is still in the 4-byte memory space allocated in the global data area, and there is no delete, and there is still no change even in his value.

His life is still lush, and always survive to the main function in the same time. So at this time, as long as we determine that the original allocated stack has been released, we can use G_PObject to let him point to other places. How do we determine that the inside of his original pointing is released, is it doing this? Void func2 () {if (g_pobject! = null) {delete g_pobject;} g_pobject = new object;} This is not good, if the FUNC function has been called before this, put the heap memory release, but g_pobject does not Set zero, then delete g_pobject; will be called again, an error. Of course, we can change the program to this: void func () {if (p! = Null) {// with g_pobject do something ... delete g_pobject; g_pobject = null;}} void func2 () {=} void func2 () {= ! = Null) {delete g_pobject; g_pobject = null:} g_pobject = new object;} Good idea, delete then is a good program habit. There is no relationship with a pointer delete that is zero. Since we often want to write a assignment statement after Delete, there is a more simple way, can only write a statement? We rewrite the operator delete () function: void Operator delete (void * p) {delete p; p = null:} void func () {if (p! = Null) {// with g_pobject do something ... delete g_pobject }} Void func2 () {delete g_pobject; g_pobject = new object;} This is not lineless, because p = null: just sets the shape P to zero, and there is no half relationship between G_PObject. Maybe you will immediately say that the pointer is coming over! Oh, I also want to, just C regulations, the Operator delete function return type can only be void, the first parameter can only be Void *, there is a second number Can only be SIZE_T. I have to think about him. Define a template function: Template void destroy (t & p) {delete p; p = null;} Then you can use: destroy (g_pobject); just look at Destroy from the literal, not clear, no What you mean, unidentified people will be wondering. I think, think about it, I want to have a good name: delete0, enough image, DELETE everyone is clear, then there is one 0, I think a uninformed person, can guess it is What mean: release and zero.

Just open a precedent, I have never seen a function name with numbers, I don't know if you have seen it? So our template function can be written: Template inline void delete0 (t & p) {delete p; p = 0;} Because it is inline, write delete0 (g_pobject); and write delete g_pobject; g_pobject = 0; the overhead is the same. Of course, I still have to write a template that implements Delete [] operation and zero, I thought I didn't know how to call Delete1, or DELETE2 is good. It is still a bit problem, it is possible to call DELETE G_POBJECT directly in a function to release the stack memory, not through delete0, so that the problem is coming, so in general, the global pointer points to a memory will not let it point to it. Other places. Everyone sees that the management of dynamic memory is really careful, and if you accidentally create an undefined behavior, what does not definite mean? It means that it is possible to happen. More terrible is that all the program is in your hand. How to test can pass, just when you deliver the program to the customer, suddenly burst on the customer's face.啦, popcorn! More, the customer has burst out after a while, and some of the important business data of the customer will be cleared, then there is a lawsuit. All this is a scour, but we can't use a pointer (there is a program that does not have a pointer to participate in the C / C program ?!!) Can only be used to use it as little as possible, can be used instead of reference . If you can't do it, you should pay attention to your pointer at any time, don't let him disaster. Adhere to the principles of allocation management of people allocated, try to assign memory by this function in a function. If a function assigned by a function is required, the output parameter should be set to Auto_PTR, or the package class yourself is written. The most important thing to keep in mind is that you can't do it to DELETE that is not assigned by you. When our pointer leaves our line of sight, give him a package first, for example, we need to pass a pointer written by others to a module written by others, you don't know what this module is, When you are a black box, put our pointer does not protect, it will be very dangerous, so pack our pointer first, so that you can only use * operations and -> exercises, but you can't delete, you can't modify it. When the pointer comes out from hell, you can solve the package, change back to our original pointer, and you can use a variety of operations. Finally, pay attention to zero after Delete. You can operate by writing two template functions. Back to our topic B: Because Str is a static local variable, static data is allocated in the global data area. The Strcpy function is then called, and the string "I Love C !" Is filled in the memory allocated above. Then, the memory in this global data area is then returned. Therefore, the STR output is OK. Last look C: C: char * mystrcpy (char * & p) // has been modified here. Also modify the declaration of the function at the program head accordingly. {Char * str = "i lvoe c !"; // has been modified here. // STRCPY (STR, "I LVOE C !"); // This has been modified here that the Return Str;} The answer is that the STR output and the P output is OK. With the above discussion, we know that you can't return the stack memory, but you can return to the stack memory, you can return static memory.

转载请注明原文地址:https://www.9cbs.com/read-13625.html

New Post(0)
CopyRight © 2020 All Rights Reserved
Processed: 0.043, SQL: 9