[C/C ++ language entry]-Go deep into Functions

Source: Internet
Author: User

In the previous article, we introduced struct. This article can finally introduce functions. Why? Because functions are very important. That's simple. Hey! At this moment, I will talk about functions because this article will relate to each of the previous articles so that the functions can be fully reflected. Then we can't wait to get into the question.

 

From the first article helloworld to the present, there is no function separation. That is our main function. The main function is also a common function, but it is usually used as the entry to the program we write. That is to say, we should execute it first. So why is it a common function? The reason is that we can change this entry by writing code. Let our program first execute our custom function without executing the main function. The specific implementation is not the content of this article. You know this is the case. Remember that the main function is not a special function. It is only considered as an entry function of the program.

 

So what is a function? In general, it is a piece of code block, which is used by us to combine scattered statements to support a function. For example, our strcpy is also a function, which is used to copy strings. It contains many statements. These statements are concentrated in the form of a function. Here, we have to emphasize that we try our best to think about the essence of a new thing, so that we will not feel abstract or unfamiliar. For example, a function is a centralized management solution of code blocks. A function name. The parameter list and return values are enclosed in braces to form a function. Although it is included, the function does not exist. When the compiler compiles Our CC ++ code into an assembly language, each function is just a piece of code. What function name, parameter list, and return values are no longer clearly visible. That is a piece of code that is concentrated together. This is what we understand. In the CC ++ syntax, the function must have names, parameters, and return values. This is understandable. Because it is a high-level language, the code will be very sensitive. It is impossible to write like this:

Function:

Void fun (void)

{

Int A = 0;

}

 

Void fun1 (void)

{

Int B = 0;

}

 

Int main (void)

{

If (...)

Fun ();

Else

Fun1 ();

Return 0;

}

 

No function:

Int main (void)

{

If (...)

Goto fun;

Else

Goto fun1;

Return 0;

 

Fun:

Int A = 0;

Return 0;

Fun1:

Int B = 0;

Return 0;

}

 

The code above is obvious, and the basic functions of the function are embodied. Modular and convenient management and maintenance.

 

After understanding the concept and essence of a function, let's look at some specific usage. First, let's talk about the return value.

Void fun ();

Int * Fun ();

Struct a fun ();

Int fun ();

Char fun ();

See the functions with different return values above. There are pointers, types, periods, characters, and struct. The C language can return only one value. If you want to return more than one value, you can only return it to the caller in the form of an address. The principles of other methods are similar. It is not described here. The return value of a function is returned at the CC ++ level with the return keyword. The returned value is obtained by the caller who needs some results. Return values are often important. The void type does not need to be returned. If you want to return it somewhere in the middle of the function, you can do this:

Void fun ()

{

Int A = 0;

Return; // return directly after a = 0 is executed. No value is returned here, but it serves as the end function.

A = 10;

}

Once a function is returned, it ends immediately regardless of whether the return value exists. Therefore, return is usually used to return values and terminate functions. We only need to look at two of the above return value types. One is to return the pointer, but to return the struct. We will investigate the nature and precautions one by one.

 

First, let's look at the returned pointer:

Int * Fun (void)
{
Int A = 100;
Return &;
}

Looking at the above program, the returned pointer format is very simple. Directly return the address of the variable. When calling the outer layer, you can:

Int * P = fun ();

In this way, P points to the address returned by the function.

 

If we want to return an array, we will talk about pointers and arrays. We also know that there cannot be function definitions like int [] Fun (void) or Int [3] Fun (void. Then we think of the similarities between pointers and arrays. Can we return the first address of an array? Then the caller obtains the first address. When we know the size of the array, we can access every element of the array one by one. Some may ask, what if I don't know the size? It is basically impossible to know the size. Can you use a macro to define your size or global variables? Why should we drill into the dead end? Right.

Therefore, the following code is available:

Int * Fun (void)

{

Int A [3] = {1, 2, 3 };

Return;

}

In outer layer:

Int * parray = fun ();

A = parray [0];

Is it very convenient. Therefore, we must be flexible when using pointers and arrays. Although the C language cannot return multiple values, we can implement this function.

 

Here, we all know that the returned pointer is ecstatic. Run it. Unexpected events occurred. Why are the returned pointers and array elements messy? Data error?

Here is a note.

 

The two return pointer functions we wrote above are all problematic. It is totally incorrect.

Why? After careful observation, the returned array and the returned a address are both temporary variables. The two functions are correct in syntax. The cause of the error is that I have returned the memory address of the temporary data. The so-called temporary variable, that is, the short life cycle, here the array and a end after the end of the function. So it is called a temporary variable. Since the lifecycle ends, the memory will be reused. It will be assigned a value again by any code or operation. This is the so-called stack memory. The stack here is not the stack in the data structure. This usually refers to the memory space for storing temporary variables. It is usually very small. The default value is 1 MB, and there is also 2 MB. This can be set by yourself. I will not talk about it here. Suppose there is such a piece of code:

Int * Fun (void)
{
Int A = 100;
Return &;
}

Int * fun1 (void)
{
Int B = 200;
Return & B;
}

 

Int main (void)
{
Int * P;
Int * P1;
Int AA, BB;

 

P = fun ();
P1 = fun1 ();

 

AA = * P;
BB = * P1;

 

Return 0;
}

 

On my machine, the two functions fun and fun1 are basically similar in code. I intentionally constructed an error that could reflect the modified stack memory. After this program ends, the values of AA and BB are both 200. Why? The reason is very simple. After we call the fun function, P points to the stack memory, for example, 0x0012ffd4. When fun1 is called, the difference between fun1 and fun is very small, the stack memory address of the Temporary Variable B is also specified to the memory address 0x0012ffd4. P1 also points to this memory address. So here AA and BB must be the same value. The reason for 200 is also very simple. The temporary variable B assigned the value of 0x0012ffd4 in the memory address to 200, which overwrites the previous 100.

 

So what if I want to change these two functions so that they will not go wrong? As follows:

Int * Fun (void)
{
Int * A = (int *) malloc (sizeof (INT ));

* A = 100;


Return;
}

In this case, the application space of the malloc function is not covered. The application space of this function is not in the stack space, but in the heap memory. We do not manually call the free function. This memory value will always exist. Know that the program ends and is recycled. Of course, in this case, the pointer is obtained in the outer layer. Remember to set it free. Otherwise, it will cause memory leakage (keep applying, do not release after use, and the memory is gradually used up .).

 

Question 1: write out the function fun1 that returns the array correctly.

 

After learning about pointer return, some may ask questions. If I want to return a second-level pointer, how can I write it? Here I will only say that the second-level pointer is also a pointer and there is nothing special. Returns the same as the pointer. Remember that the pointer variable can also be a temporary variable. If you are not clear about the details, we suggest you read the previous two articles on pointers.

 

Okay, the return pointer is finished, and then the return struct.

As you read the returned pointer above, you may be wondering. Struct is a set of group members, similar to an array. Do we have to use a pointer to return the first address? Or are there other methods? First look at the program:

Struct
{
Int;
Int B;
Int C;
};

 

Struct a fun (void)
{
Struct a = {1, 2, 3 };
Return;
}

 

Int main (void)
{
Struct a ret = fun ();

Return 0;
}

 

With such a piece of code, we want to return the value of temporary struct variable A to the Temporary Variable RET in the main function. Here I deliberately emphasized the word temporary variables. I hope this will not cause misunderstandings. Although a is a temporary variable, I return the variable A to ret instead of pointing to it. Instead, it is a copy. This means that the three member values of the Temporary Variable A are copied to the corresponding member of the RET variable. It is different from pointer. As we mentioned earlier, the C language cannot return multiple values. To return values, use pointers. So here I didn't use the pointer to clearly return three values: 1, 2, and 3. Why? The answer may not be suitable here. I will first talk about it here and understand it. If you cannot understand it, remember that the return of the struct variable can return multiple values, so that the struct variable is a value and you do not need to think of its members. In essence, how does a struct variable return three values?

 

The reason is that the C language helps us do a lot of things by default. In the background, the return is only an address, that is, the first address of struct variable, this first address is not stored in our Defined variables, but passed through the CPU register. Then assign the value of the memory address pointed to by the Register to member A of the RET variable, and then the address + offset pointed to by the Register (4 here, all are int type) it is the memory address where B is located, and then the value of B is assigned to B in Ret. C is also a truth. In this way, the value is passed. We can understand that after the compiler is compiled, the program will build a temporary struct in the memory. Copy the values in the struct variable to be returned by the function to this temporary struct. We cannot see this struct. After the function is executed, assign the value of this temporary struct to our receiving variable. It may be a bit difficult to understand here. What is a temporary struct? Didn't I always emphasize the essence. The struct is a continuous memory space. Here, the struct occupies 12 bytes, so I can build a 12-byte space somewhere in the memory. The value of the three Members that place the struct. This is called a temporary struct.

 

I have to remind you again. Here we want to return multiple struct variables, we can also use pointers. The principle is similar to the principle returned by the above basic type pointer. There are also temporary stack memory problems. The return pointer (return address) and return value (copy) must be clearly distinguished.

 

Okay. The return value is complete. The following describes the parameters.

There can be multiple parameters, and there can be variable parameters. For example, the commonly used printf function is an variable parameter. That is, the number of dynamic parameters.

The fixed number of parameters is the same as the number of parameters. Here I will only list one parameter or two parameters.

Void fun (int * P );

Void fun (int );

Void fun (void );

Void fun (int * P, int size );

I did not write the return value above. It is unnecessary to return the value. Before learning about the parameters, let's take a look at an example:

Void fun (INT var)
{
Var = 100;
}

Int main (void)
{
Int A = 1;
Fun ();

Return 0;
}

In this program, we call the fun function and try to change the value of. Unexpectedly, the value of a changes after the fun function is called. Why? Many beginners may be confused. Or you just need to remember that this will not change the value of. Only by knowing the nature of a thing can we remember it better and never remember it. Let's talk about the reason why a is not changed.

 

You may have heard of value transfer, address transfer, and reference transfer. References are passed in this article. It involves the related concepts of C ++. We will talk about references later.

So let's talk about value transfer first.

 

We learned from the above content that the stack memory is the place where temporary data is stored. The temporary variables in the function are all placed here. Here, you have to understand that the parameter is passed. Whether it is a pointer or a value. Before calling a function, the program pushes parameters to the stack space inside the function. This means that the function will process these parameters in front of the temporary variables in the function. Here, the process of pushing a parameter into the stack space inside our function is called transfer. The value in the pressed place (memory address) is usually called a copy of the parameter. Don't think of FB in the game here. The conclusion means that when we pass parameters with the function, we will push the parameters one by one into the stack memory where the function is located. The press here can also be understood as writing values into the stack memory.

 

The above fun (a) is to first press the value of a into the stack memory, such as 0x0012ffec memory. The value below the memory address is 1, which is usually referred to as the copy of A (clone body ). Then run Var = 100 in the function. The memory address of the VaR value here is 0x0012ffec, that is, the memory address of the passed parameter. All this is done by the compiler. Then, we assign a value of 100 to the 0x0012ffec memory address. Now, VaR is changed to 100. Then the function fun is executed. By now, you may know why the value of a will not change. The reason is that the function only knows to change the value in the memory address 0x0012ffec. changing this value does not affect a, because a is a local variable of the main function, the memory address of A is not 0x0012ffec. The address 0x0012ffec can pass the value of a into the function because the value 1 of A is copied to the memory of 0x0012ffec when the parameter is pressed. Note that this is a copy.

 

So here we thought about how to change the value of? As follows:

Void fun (int * var)
{
* Var = 100;
}

Int main (void)
{
Int A = 1;
Fun (& );

Return 0;
}

The pointer can be used to change the value of. Everyone is confused again. Why can the pointer be changed here? The reason is the same as above. First we pass in the memory address of a, for example, 0x0012ffff, and pass the address to the function. We can see from the above that although it is the transfer address, but it still pushes the address into the function's internal stack space in front of the value, for example, to the memory 0x0012eeee. Note that each function has its own stack space for its own use and is discarded when used up. Therefore, the memory address entered here cannot be the same as the memory address of variable a itself. Then let's look at the fun function, which is a pointer value operation and then assigned a value of 100. Let's take a look at the process. First, var we know that its memory address is 0x0012eeee (which is arranged by the compiler above), and the value in this memory address is 0x0012ffff. Var is a pointer. In the previous pointer article, we know that var has its own memory address (0x0012eeee ), it saves the memory address it points to (0x0012ffff ). Here, the memory address is the address of the variable passed in. When we indirectly access (* var), we actually operate on the variable itself. Therefore, the address pointing to a will change its value to 100.

 

In this example, we have already discussed the address transfer method without saying hello. Address Transfer is to pass the address of a variable to the function. When the function accesses this parameter, it reads and writes the address value of the external variable. Therefore, you can change the value of the input parameter.

 

Question 2: If a is a pointer in the above program, we will pass a to the function fun, and then change the pointer pointing (pointer value) in the fun function ). Will the pointer change? Why? (Note: the principle is the same as above. If necessary, use a second-level pointer for address transmission)

 

When it comes to arrays, we have to think that if we want to upload a one-dimensional array to the function, for the function value or write value. What should I do?

Void fun (int *)
{
A [0] = 100;
A [1] = 100;
A [2] = 100;
}

Int main (void)
{
Int array [3] = {1, 2, 3 };
Fun (array );

Return 0;
}

In the above Code, we intend to change the value of array to 100. Our goal is achieved, and the results are all normal. Why? Some readers may have been confused by the above value transfer and address transfer. Here we don't need to think much about it. We should know that the first address of the array is passed in here. In the function, we will modify the value in this address and add the offset one by one. This operation is also done through the address. The principle is the same as above. The fun function here is an array that I know has three elements. If I do not know, we should add another parameter for the number of elements in the array. This is safe and decent. For example: void fun (int * parray, int size );

Fun (array, 3); in this way, the function will not be afraid that the read and write operations are out of bounds.

 

Question 3: how to upload a two-dimensional array to the function?

 

 

Here is one of the terrible consequences of crossing the border:

Void fun (void)
{
Printf ("I'm come in !!! /N ");
}

 

Int main (void)
{
Int array [1] = {1 };
Array [3] = (unsigned INT) fun;

Return 0;
}

 

The above simple program has explained the basic principle of a classic buffer overflow attack. First, explain the program. Here an array is defined, which has an element. The following sentence array [3] = (unsigned INT) fun; here, I intentionally assigned the address of the fun function to the second memory address behind the array. Occupies 4 bytes. For this purpose, you will know when you run it. It is amazing that I entered the fun function without calling the fun function and output I'm come in !!! String. Many people may be stupid. Why? I have not called it here.

 

The reason is very simple. After each function is executed, it will jump back and return to the next statement that calls this function for further execution, the reason why a function can be redirected back is that the Code address we will return when calling the function is saved to the function stack memory. The purpose of writing arrays out of bounds here is to change the returned address value to the address of the fun function of my target function (the function also has the first address ). Here, the first address of the forced type conversion fun function is an unsigned integer that overwrites the return address of the main function. In this way, when the main function returns, it will jump to the fun function and execute this function. Output string. We can think of this fun function as a function that our hackers want to operate on. Here is the basic principle of the classic "buffer overflow attack.

 

If I am not an array, but a character array, we didn't check the length during strcpy. The hacker modified the string parameters passed in by the function to overwrite the return address, the covered content is the address of the function implemented by hackers themselves. Our program will call its functions without knowing it. Of course, after the output is executed, the fun function does not call the function normally, but no one pushes the address to the function, finally, the address returned to the error will crash. Here I have not processed the stack balance and return address. It will not crash after processing, and will be as smooth as the normal process.

 

The above mentioned out-of-bounds buffer overflow and disorderly tuning functions are also used to introduce function pointers. In the above example, we first realized that functions also have their own addresses. Since there is an address, the pointer must be true. Since it is a pointer and a common function, there is no problem in how to convert the pointer at will. This is also the charm of CC ++. I simply converted it into an unsigned integer and overwritten the return address. Is it convenient? Then let's look at the regular function pointer definition:

Void fun (void)
{
Printf ("I'm come in !!! /N ");
}

 

Int main (void)
{
Typedef void (* pfun) (void); // defines the function pointer. The typedef alias is used here, And the pfun is declared as void to return. No pointer to the parameter Type Function
Pfun = fun;

(* Pfun )();

Pfun (); // both call methods are the same

Return 0;
}

 

We already know the definition of function pointers. The syntax is very simple. Define a function pointer pfun and assign the value to the address of the fun function. The function name also indicates the function pointer. This pointer is the Code address starting with the fun function. Here is the Code address. In our EXE, each sentence of code has its own code address. The code value here is to compile each instruction. We will not investigate it here. We only need to know that the function also has the first address. You can assign a value to a function pointer or even any pointer. After assigning a value to the function pointer, we can call it like (* pfun) (); pfun. It is no different from function call. Assume that you assign the fun function to a void * pointer P:
Void * P = (void *) fun;

P (); // Error

This will be wrong, so let alone the reason. Everyone in the world knows.

 

Function pointers are flexible, and can also be returned by parameters. It is no different from a common function.

 

Question 4: define a function pointer with parameters and returned values and call it.

 

It is also necessary to take the function pointer as a parameter:

Typedef void (* pfun) (void );

Void fun (void)
{
Printf ("I'm come in !!! /N ");
}

 

Void call_func (pfun)
{
Pfun ();
}

 

Int main (void)
{
Call_func (fun );
Return 0;
}

 

The code above reflects passing the function pointer as a parameter to a function so that the function can be executed in another place. This process is usually called callback. Fun is called a callback function. We pass the fun function pointer to call_func, and then call_func calls this fun function. The principle is clear.

 

Callback functions are used in many large projects. The most direct one is our Win32 message callback function. We need to register our own defined functions for the operating system. The registration here is actually a function pointer provided by the operating system to us. We will assign the provided function pointer to the pointer of our custom function. The operating system is constantly calling this function pointer. Therefore, the operating system can call our custom functions. You can write such a call model on your own. For example, a function pointer linked list stores many function pointers. We can traverse and call all function pointers in this linked list. All these pointers are assigned to the functions we want to call.

 

It is worth noting that you must be careful when using function pointers, such:

 

Typedef int (* pfun) (void );

Void fun (void)
{
Printf ("I'm come in !!! /N ");
}

 

Int call_func (pfun)
{
Int A = pfun ();
Return;
}

 

Int main (void)
{
Int ret = call_func (pfun) Fun );
Return 0;
}

 

I forcibly convert the fun function into a function pointer of the int return type and then call it. In this way, the RET value will be discarded. Unpredictable. The reason is simple. The fun function does not return values. Here, we will not explain the specific value of the returned value here, knowing that this is the case. If the conversion is not forced, the compiler will only give a warning. This method is absolutely incorrect. Therefore, when using callback functions, we must pay attention to the declared function pointer of the parameter.

 

In addition, variable parameters of the function are not mentioned here. This is not the focus, but the syntax. You can understand it by checking the information.

 

Now, we have finished introducing the function. Everyone understands. A little long. I wrote it for about five hours .... Rest ..

 

[C/C ++ entry series]

[C/C ++ language entry] -- Preface

[C/C ++ language entry] -- Thinking about helloworld

[C/C ++ language entry] -- Basic Data Type

[C/C ++ language entry]-debugging Basics

[C/C ++ language entry]-deep pointer

[C/C ++ Introduction] -- array and pointer

[C/C ++ Introduction] -- Structure

[C/C ++ language entry] -- go deep into Functions

[C/C ++ language entry]-bit operations

[C/C ++ language entry] -- analyzes floating point numbers

[C/C ++ language entry] -- File Operations

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.