Some opinions about functions and character pointers

xiaoxiao2021-04-09  345

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.

Please see the following applet: * /

#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 * mystrcpy (char * p) {char STR [15];

STRCPY (STR, "I LVOE C !");

P = Str; Return Str;}

/ / Can I get the correct result in this STR output and P output statement in this program?

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) // 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

P = Str; Return Str;}

Ok, let's take a look, let us first see the original title: char * mystrcpy (char * p) {char STR [15];

STRCPY (STR, "I LVOE C !");

P = Str; Return Str;}

We know that the Str is a local variable, and it is definitely not to return a local address, but the P output does not work? 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;}

The STR output can be obtained correctly, and the 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 this function may be mobilized multiple times, such as

For (int i = 0; i <1000000; 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. such as:

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 here 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 through 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, and delete is called in the destructor of the template class [].

Back to our topic, P output is not.

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;}

Here, when the MySWAP function is mobilized, the memory is allocated in the stack and stores 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 modifications:

INT * myswap (int * vaal1, 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 mimped, the memory, pointer variable VAL1, VAL2 of 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 * vaal1, int * avl2) {result = * VAL1;

* VAL1 = * VAL2;

* VAL2 = * VAL1;

Result = * VAL1 * VAL2;

Return & Result;

When Return & Result is encountered, the overall variable RESULT allocates the address of the memory of the memory to the TEMP, followed by the stack space, (cannot recover the result space, because it is not in the stack in the allocated memory), then Assign the contents of TEMP to Sun, so 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 transfer reference, change the program to:

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 called, the stack space is assigned: 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 you encounter Return Result;, the address of the global variable RESULT will give Temp, collect the stack space, and give the TEMP to Sun, the content of Sun is the address of the global variable Resul, but the compiler knows that he is a reference In the future, all the operations of it are operated in the number of addresses 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 transmission, but sometimes our program requires returns an object, and when the reference is not suitable, the following cases have compared:

Class Object;

Object Func (Object & A, Object & B) {// do something

Object Sun (A B);

Return sun;

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 put int RES (X1 X2); RETURN RES;

Write directly: Return Int (x1 x2);

Go back to our topic, why is the P output in A?

Char * mystrcpy (char * p) {char * str = new char [15]; // This has been modified.

STRCPY (STR, "I LVOE C !");

P = Str; Return Str;}

When the function char * mYStrcpy (char * p) is called, the stack space is assigned: return the address, the space parameter P, then put the contents of the real parameters P (ie: null, note, the contents of P, Not the address of the P itself), then assign the space of the STR pointer, then assign 15-byte space in the heap, the STR points to the first address of the heap space, then call the STRCPY function, so that the content in the heap is "I Love C ! ", Then assigning the STR to the shape 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 of MySTRCPY in the main function to: MySTRCPY (& P), so that the PO output can be given the correct answer.

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 In case, the STR output can result in the correct answer. 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; such as P is a pointer to a custom type, the false code of its implementation is as follows:

IF (p == null) {return;}

// Call the destructor of the object to be directed;

P-> deStruction ();

Free (p);

We have seen it, for a pointer P, which is NULL P, regardless of how the delete P is not a problem, but for a pointer P that does not equal Null, the delete p will have an 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, the global data area is allocated to the variable g_pobject, then assign the sizeOf (Object) size memory in the pile area, then call the Object constructor, construct an Object object, then put the first address of the heap space COPY A memory space stored in 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;}

Don't do this, if the FUNC function has been called before this, let the pluck memory release, but g_pobject does not zero, then delete g_pObject; will be called again, an error. However, 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 () {

IF (g_pobject! = null) {delete g_pobject;

g_pobject = null:} g_pobject = new object;

Good idea, delete then is a good programming 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 good 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 this: destroy (g_pobject);

Just look at Destroy from the literal, just clearing it, does not set it up, 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; 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. 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

P = Str; 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. The STR returned in the current situation is the memory of that area?

C specifies that anything that is used to use a pair of two quotes is considered to be a string from being used in addition to the initialization character array.

For example: void func () {char str1 [12] = "i love c !"; // Here is a string constant, equivalent to char str1 [12] = {'I', '', // 'L', 'o', 'v', 'e', ​​'', 'c', ' ', ' ', '!', '/ 0'}; char * STR2 = "i love c !"; //// Here is a string constant. }

We know that constants are memory allocated in the global data area. So the compiler sees when compiled: char * str = "i love c !"; First assign 12 bytes of memory in the global data area, and sequentially fill in characters' I ',' ',' L ',' O ',' v ',' e ',' ',' c ',' ',' ','! ',' / 0 ', and put this memory address to the string "i love C ! ", When the MYSTRCPY function is called first, the spatial variable P is first assigned, then the address of the real parameters P (because the character pointer is passed is a reference) COPY gives the metallographic P, which is then the other operation of the ginseng P. For real parameters P. Then allocate the space of the pointer variable STR and initializes "I Love C !". I just said that "I Love C !" Is the first address of a memory in the global data area. So actually put the first address of a memory in the global data area in the memory space of the STR. Then, the value of the STR COPY was passed, and it was said that the operation of the reference P is the operation of the real parameter P. So the P also points to the memory address in the global data area. Finally, returning the Str, which is to copy the value of the STR to the temporary variable Temp. Temp also points to the global data area, so cout << mystrcpy (p) << Endl; equivalent to cout << Temp << Endl; Temp points to the global data area. So OK!

Write the program into direct return "i love c !"; It is also possible, don't forget the string constant representing the first address of a memory in the global data area.

Finally, I said:

#include using namespace std;

Void func (int * p) {cout << p << endl;

Delete P;

}

INT main {INT i = 0; int * p = & i;

Func (P);

DELETE P; / / Will it be wrong?

p = null;

Return 0;}

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

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