This document gives friends that I am generally also in the road of C language.
Note: The compilation environment of the test program in this article is Win2000 and VC6.0
origin:
As a programmer, I didn't write a variable function. I believe that most of my friends have not involved, or my realm is too low. So why I want to go to this floor? Because curiosity!
I am a very inert person, I have learned the variable variable function, too lazy to go deep, but it is three points ("..." in the parameter list when the function declares "...") is deeply reflected in my memory. And it is with a number of shining question marks. However, at yesterday, when I read the high myth of a monarch, it came again. My qualification is really not enough, because some Jun is talking about it when it is talking about it, it is a macro definition in
Broken question:
However, the so-called "implementation" has never had a process, but I just want to confliterate it, because it is just a good end of the programmer service.
Still starting with the Printf we are familiar with:
If you are a C language programmer, no matter what your beginner is still a high master, it will not be strange for Printf, and you have used countless times. I have said that I am a very inert person, so every time I use Printf, I have said in accordance with the textbook, and I have never asked why, this is the so-called "familiar".
In fact, the PrintF function is a typical parameter variable function. In the case where the first parameter is guaranteed is the condition of the string, you can lose the number of arbitraries of any legal type. As long as you use the corresponding format string in the first string parameter, you can output the correct value. Isn't this a very interesting thing? Then how did it do?
1. First, how to get the value of the parameters. For a general function, we can get the identifier in the parameter list by parameters. However, the variable variable parameters of the parameter variable functions are no parameter identifiers. It is only "...", so it is impossible to pass the identifier, and we only have another way.
We know that the function call will assign a stack space, and the stack structure in the function call mechanism is shown below:
| ... |
------------------
| Parameter 2 | ------------------ | Parameter 1 | ------------------ | Return Address | ---------------- | Call function running state | ------------------
It can be seen that the parameters are continuously stored in the stack, that is, however, we can access those variable parameters through the pointer as long as we get the address of the previous parameter of variable parameters. But how do you get the address of the previous parameter of variable parameters? I don't know if you notice that there is no, the parameter variable function must have a parameter before variable parameters, and use the identifier, and is usually declared as a char * type, and the PrintF function is no exception. In this way, we can get the address by the identifier corresponding to this parameter, thereby accessing other parameters. We can write a test program to try: #include
Void va_test (char * fmt, ...); // parameter variable function declaration
void main ()
{
INT A = 1, c = 55;
CHAR B = 'b';
VA_TEST ("", b, c); // Test with four parameters
}
Void va_test (char * fmt, ...) // parameter variable function definition, pay attention to the first parameter is char * FMT
{
CHAR * P = NULL;
p = (char *) & fmt; // Note not pointing to FMT, but pointing to & FMT, and forced transformation to char * so that one byte access
For (int i = 0; i <16; i ) // 16 is through the calculated value (parameter number * 4 bytes), just for testing, temporary
{
Printf ("%. 4D", * p); // outputs the value of the P pointer points to the address
P ;
}
}
The result of the compilation operation is
0056 0000 0066 0000 | 0001 00 00 00 00 00 0098 00 00 00 0000 0000 0000 0000 0000
It can be seen from the operation results, and the value of variable parameters can be obtained one by one by way of this manner.
As for why it is usually declared as a char * type, we slowly appear.
2, how to determine the parameter type and quantity
Through the above method, we first solve the problem of obtaining a variable parameter value, but for a parameter, the value is very important, and its type is equally raised, and for a function, the number of parameters is also very important, otherwise it will produce one The series of troubles come. We cannot get any information about the type of any information and parameter number of information by accessing the stack of storage parameters. I think you should think of - using a char * parameter. The Printf function is implemented, it puts the rear variable parameter type in the character array pointed to by the char *, and identifies through% to distinguish between other characters, thereby determining the number of parameters. . In fact, what kind of way to reach this depends on the implementation of the function. For example, define a function, presence its variable parameter type is int, and the fixed parameters can be used to replace the char * type with the int type, as only the number of parameters can be obtained.
3, the words home
I thought here, the probably contour has already been present. I originally thought about this (my inertia), but I think that if I don't have practicality, it may be a bunch of waste. I will have such words, and I will continue.
I am comparing the macro defined by those unfamous, so there is no VA (Variable-Argument) macro defined in
Open the
1) TYPEDEF CHAR * VA_LIST;
2) #define _intsizeof (N) ((SizeOf (N) SizeOf (Int) - 1) & ~ (SIZEOF (Int) - 1))
3) #define va_start (ap, v) (ap = (va_list) & v _intsizeof (v))
4) #define va_arg (AP, T) (* (t *) ((AP = _INTSIZEOF (T)) - _INTSIZEOF (T)))))
5) #define va_end (ap) (AP = (va_list) 0)
We view one by one:
The first one I don't want to say, the type is defined. The second is quite a little, we have to understand it, because the two key macro definitions are used. I don't know if you are not careful enough, have you found it in the test program above, the second variable parameter is clearly a char type, but 4 BYTEs in the output result. Does all parameters account for 4 BYTE space? That is the parameter of the Double type, and it is not lost data! If you don't have trouble, then do a test, use a Double type (length 8Byte) and a long double type (length 10byte) in the test program. What is found? The Double type accounted for 8byte, and Long Double accounted for 12Byte. It seems that it is a 4 most integer. It has to be taken out of another conceptual "alignment", so-called alignment, the intel80x86 machine is required to require the address of each variable is a multiple of sizeof (int). It turns out that we have made wrong, the parameters of the char type only account for 1byte, but its back parameters can only skip 3byte storage because the relationship between the alignment can only be skipped, and the 3byte is wasting. So why is it going to be? Because in alignment, the CPU's operational efficiency is much more (for example, what is the following example is that I am extracted from the Internet, I don't remember.
Example: As shown below, when a LONG type (such as long1 in the figure) is aligned with the memory word boundary in memory, the CPU Access this number only needs to access a memory, and when a long type (as shown LONG2) When the location in memory spans the word boundary, the CPU Access this number requires multiple access memory, such as I960cx access to such a number to read the memory three times (a Byte, a short, a Byte, by CPU) Micro-code execution, transparent software, so the operating efficiency of the CPU is much more fast in alignment.
1 8 16 24 32 ---------------------------
| long1 | long1 | long1 | long1 |
---------------------------
| | | | | Long2 |
---------------------------
| long2 | long2 | long2 | | |
---------------------------
| ....). It seems to be a bit far, but it helps to understand _intsizeof (n). The bit operation is the stuff for me. Single bit operations should be paid, and such an expression is stunned in front of it. How to do? Rookie's own way. (to be continued)