Wedge
Last year, he published an article "array reference" in the vckbase/C ++ forum to avoid "array downgrading". At that time, I couldn't fully understand the meaning of this usage; after a year, my knowledge has been refined, and I have finally become enlightened by this Article. So I have become an article by thinking about what I know. I hope this article will be helpful to new users, and I hope you will not leave any comments if you find any omissions in this article.
The story originated from the two demos provided by Brother Zhou Xingda. To save some time, I combined the two demos into one to illustrate the same problem:
# Include <iostream>
Using namespace STD;
Void foo1 (INT arr [1, 100])
{
Cout <"pass by pointer:" <sizeof (ARR) <Endl;
}
Void foo2 (INT (& ARR) [100])
{
Cout <"pass by reference:" <sizeof (ARR) <Endl;
}
Void main ()
{
Int A [100];
Cout <"in main function:" <sizeof (a) <Endl;
Foo1 ();
Foo2 ();
}
The running result is as follows:
In main function: 400
Pass by pointer: 4
Pass by reference: 400
This Code shows that if the array parameter is in the form of an array name (or pointer form, as discussed below), the length of the original array will not be obtained using the sizeof operator; if the original array reference method is passed, no problem occurs.
This code is really hard to understand, the dozens of rows involve four major issues: the relationship between the form and the real parameter, the relationship between the array name and pointer, the meaning of the reference, and the relationship between the sound name and expression, as long as one piece of code cannot be understood, or is not correctly understood, the above Code cannot be understood. This article starts with these four problems, solves these four problems first, and then explores the above Code. Although this seems complicated, I think it is a road to understanding and learning from the root.
I. Relationship between function parameters and real parameters
Void Foo (int );
Foo (10 );
Here, a is called the form parameter (parameter), and 10 is called the actual parameter (argument. What is the relationship between form parameters and form parameters? They are the relationship of value assignment. That is to say, the process of passing the real parameter to the form parameter can be seen as the process of assigning the real parameter to the form parameter. In the preceding example, if the argument 10 is passed to the parameter A, it is equivalent to a = 10. (Because there are many data types, it is impossible to give an example comprehensively, so we will not give an example here. If you think it is hard to understand, write a sample in VC to debug various data types, and you can verify this conclusion .)
2. Relationship between array names and pointers
This is a historical issue. In C, array names are treated as pointers. More specifically, the array name is the pointer to the address of the first element of the array, and the array index is the offset from the address of the first element of the array. It is very important to understand this. This is why many array applications have problems. That is why the array in C Language starts counting from 0, because its index is better mapped to the offset. In C language, if an expression with an array name is encountered during compilation, the array name is replaced with a pointer. the compiler cannot even distinguish between a [4] and 4 [! * 2
However, pay attention to the following points:
Int A [100];
Int * B;
The two are not equivalent. The first sentence declares array A and defines this array. It has 100 int elements, and sizeof (a) gets the memory size occupied by the entire array, is 400; the second sentence only declares and defines an int-type pointer. sizeof (B) will get the memory size occupied by this pointer, which is 4. Therefore, although array names are generally processed as pointers in expressions, there are still gaps between array names and pointers, at least a = & A [0] But sizeof ()! = Sizeof (A [0]).
In addition, the ansi c standard also explicitly stipulates that, in the Declaration of function parameters, the northern part of the array name is treated as a pointer to the first element of the array. Therefore, the following writing forms are equivalent:
Void foo1 (INT arr [1, 100]) {}
Void foo2 (INT arr []) {}
Void foo3 (int * ARR ){}
C ++ is fully compatible with C language as much as possible, so the syntax of this part is the same.
Iii. Significance of reference
"Reference" is the concept introduced in C ++, which is not in C language. It aims to replace pointers in some ways. If you think that references and pointers are not quite different, you will surely report an injustice for your fingers. There is a lot of emotion like "Life is bright, he shengyu". However, references do have new characteristics, it is true that the performance and pointer are different in many places. This article is an example. When using reference, we need to grasp this most important point, which is also the biggest difference between it and the pointer: Once the reference is defined, it is tightly integrated with the variables referenced by it, if they are not separated, any referenced operations are reflected in the variables it references. The pointer is just another way to access the variables it points to. Although there is a correlation between the two, but it is not as inseparable as references. :)
# Include <iostream>
Using namespace STD;
Void main ()
{
Int A = 10;
Int & a_ref =;
Int B = 20;
// Initialization is required when a reference is defined, indicating that the reference is inseparable from the element to which it points
// Int & B _ref; // error c2530: ''B _ ref'': References must be initialized
Int & B _ref = B;
Int * P;
Int * q;
// The following results confirm that once a reference is defined, it cannot point to another target;
// Assign a quote B _ref to another quote a_ref. In fact, B is assigned to.
Cout <a_ref <"<B _ref <Endl;
A_ref = B _ref;
Cout <a_ref <"<B _ref <Endl;
Cout <A <"" <B <Endl;
Cout <Endl;
// Even if the address of a referenced a_ref is obtained, it is also the address of. It has already been "cool :)
P = &;
Q = & a_ref;
Cout <p <"<q <Endl;
Cout <Endl;
// The following code shows the differences between pointers and references.
P = &;
Q = & B;
Cout <p <"<q <Endl;
P = Q;
Cout <p <"<q <Endl;
Cout <Endl;
System ("pause ");
}
The following is the running result for your reference:
10 20
20 20
20 20
0012fed4 0012fed4
0012fed4 0012 febc
0012 febc 0012 febc
Iv. Relationship between declarations and expressions
Here, we want to explain that a declaration can be analyzed as an expression and declared according to the operator priority order in the expression. For example, if int (& ARR) [100], you must first find the declarative arr, then & arr indicates that arr is a reference. What references? Looking at the brackets, [] indicates this array. 100 indicates that this array has 100 elements. The previous int indicates that each element of this array is of the int type. Therefore, this statement means that arr is a reference to an array with 100 int elements. If you think this kind of understanding is obscure, you may wish to use typedef to simplify the complex operator priority relationships in the declaration. For example, the following form can be well understood, the effect is the same as that in the original example:
# Include <iostream>
Using namespace STD;
Typedef int intarr [100]; // This, this... can also be understood using expressions. Is it a bit "GNU is not UNIX?
Void Foo (intarr & ARR) // Noh. In this case, you can see that a reference is passed in.
{
Cout <"pass by reference:" <sizeof (ARR) <Endl;
}
Void main ()
{
Intarr A; // define a with a type alias
Intarr & a_ref = A; // use a type alias to define reference a_ref
Cout <"in main function:" <sizeof (a) <Endl;
Foo ();
System ("pause ");
}
Finale
It's okay for everybody to feel like it's about to end. Let's take a look at it. Take a look at the following program:
# Include <iostream>
Using namespace STD;
Void main ()
{
Int A [100];
Int * pA =;
INT (& a_ref) [100] =;
Cout <sizeof (a) <Endl;
Cout <sizeof (PA) <Endl;
Cout <sizeof (a_ref) <Endl;
System ("pause ");
}
How about it? Is it natural for the output results? If so, it would be easy. I just want to summarize the lesson! Pai_^
The array name is often regarded as a pointer to the first element a [0] address in the expression, but in sizeof (A), the returned result is the memory size occupied by array; pa is the pointer to a, and it also points to a [0]. However, in sizeof (PA), the returned result is the size of the memory occupied by the PA pointer, because the pointer PA is not closely integrated with array A, it is the second method to access array a. the reference a_ref is a reference to array, just like "evil spirits", once the attachment is attached, you cannot discard it. Any operations on it are reflected in. In the first example of this article, the Operation added to this example is the transfer of the real parameter to the form parameter of the function. As we have mentioned above, the transfer from real parameters to form parameters can be seen as assigning values to form parameters. Therefore, the actual operation process of the first example in this article is the same as that in the last example. Therefore, it is not the function that gives the array a "downgrading", but it should be like this in its original version, so it is not surprising.
: P
In PS: in C language, there is no reference. How can this problem be solved. Below are several common practices:
When an array is passed, a parameter is added to record the number or length of elements in the array. Main (INT argc, char ** ARGs) is the same method. This method can also prevent overflow and ensure high security.
Mark the last valid element of the array to indicate that the array has ended. Char array table in C Language