Array of answers to common questions in C language programming

Source: Internet
Author: User

(If you do not look at the time interval, you will forget to use it in actual use --- using the C language array, XOR or pointer). Repeat it and forget it for query.

One of the reasons why C treats arrays is that it is popular. The C language is very effective in array processing, for the following reasons:
First, except for a few translators who exercise caution to make some complicated rules, the array subscript of C language is processed at a very low level. However, this advantage also has a negative effect, that is, when the program is running, you cannot know the size of an array or whether an array subscript is valid. The ANSI/ISOC standard does not define the use of an out-of-Boundary behavior. Therefore, an out-of-boundary mark may cause the following consequences:
(1) The program can still run correctly;
(2) The program will terminate or crash abnormally;
(3) The program can continue to run, but the correct results cannot be obtained;
(4) other cases.
In other words, you don't know what the program will do later, which can cause a lot of trouble. Some people criticize the C language by grasping this point and think that the C language is just a high-level assembly language. However, despite the terrible performance of C program errors, no one can deny that a carefully written and debugged C program runs very fast.
Second, arrays and pointers work in harmony. When an array appears in an expression, it is equivalent to the pointer pointing to the first element in the array, so the array and pointer can be almost interchangeable. In addition, pointer usage is twice faster than array subscript usage (see the example in 9.5 ).
Third, passing the array as a parameter to the function and passing the pointer pointing to the first element in the array to the function is complete ·
Equivalent. When passing an array as a parameter to a function, you can use the value transfer and address transfer methods. The former needs to completely copy the initial array, but is safer. The latter is much faster, but be careful when writing the program. Both C ++ and ansic have the key word const, which can make the address transfer mode as safe as the value transfer mode. For more details, see the introduction at the beginning of "pointer and memory allocation" in Chapter 2.4, 8.6, and 7th.
This relationship between arrays and pointers can cause confusion. For example, the following two definitions are identical:
Void F (chara [Max])
{
/*...*/
}
Void F (char *)

/*...*/
}
Note: Max is a value known during compilation, for example, the value defined by the # define pre-processing command.
This is the third advantage mentioned in the previous article and is also well known to most C programmers. This is the only case where arrays and pointers are identical. In other cases, arrays and pointers are not identical. For example, if it is defined as follows (it can appear anywhere other than the function description:
Char A [Max];
The system will allocate a maximum of characters of memory space. As follows:
Char *;
The system allocates the memory space required by a character pointer, which may only contain 2 or 4 characters. Suppose you define the following in the source file:
Char A [Max];
However, the header file is described as follows;
Extern char *;
It will lead to terrible consequences. To avoid this, the best way is to ensure the consistency of the above description and definition. For example, if the following definition is made in the source file:
Char A [Max];
The following describes the corresponding header files,
Externchar A [];
The preceding description tells the header file a to be an array rather than a pointer, but it does not indicate how many elements exist in array A. The description of the type is called an incomplete type. It is common to describe some incomplete types in the program, and it is also a good programming habit.

Is the subscript of array 9.1 always starting from 0?
Yes, for Array a [Max] (Max is a value known at compilation), its first and last elements are a [O] And aLMAX-1, respectively ). In other languages, the situation may vary. For example, in basic languages, the elements of array a [Max] are from a [1] to a [Max], in PASCAL, both methods are feasible.
Note: A [Max] is a valid address, but the value in this address is not an element of array A (see figure 9. 2 ).
These differences can sometimes lead to confusion, because when you say "the first element in the array", it actually refers to "the lower mark of the array. The "first" here means the opposite of the "last.
Although you can create an array whose subscript starts from 1, you should not do this in actual programming. The following describes this technique and explains why it should not be done.
Because pointers are almost the same as arrays, you can define a pointer so that it can reference all elements in another array like an array, however, when referenced, the subscript of the former starts from 1:
/* Don't do this !! */
Int A0 [Max],
Int * a1 = a0-1;/* & A0 [-1 )*/
Now A0 [0] and A1 [1) are the same, while A0 [MAX-1] and A1 [Max] are the same. However, this should not be done in actual programming because of the following two reasons:
First, this method may not work. This behavior is not defined by ANSI/ISOC standards (and should be avoided), and & A0 [-1) may not be a valid address (see 9.3 ). For some compiled programs, your program may not have problems at all; in some cases, your program may not have problems for any compiled programs; however, who can ensure that your program will never go wrong?

Second, this method deviates from the conventional C language style. People are used to the working method of the array sub-mark in C language. If your program uses another method, it will be hard for others to understand your program. After a while, it may be hard for you to understand this program.

See:
9.2 can I use the address of the first element behind the array?
9.3 why be careful with the addresses of those elements behind the array?

9.2 can I use the address of the first element behind the array?
You can use the address of the first element after the array, but you cannot view the value in the address. For most compilation programs, if you write the following statement:
Int I, a [Max], J;
Both I and J may be stored in the address behind the last element of array. To determine whether I or J is followed by array A, you can compare the address of I or J with the address of the first element after array, determines whether "& I = & A [Max]" or "& J = & A [Max]" is true. This method is usually feasible, but cannot be guaranteed.
The crux of the problem is: If you store some data into a [Max], it will often destroy the data that followed array. Even if you look at the value of a [Max], it should be avoided, although this generally does not lead to any problems.
Why is & A [Max] sometimes used in C programs? Many C programmers use pointers to traverse all elements in an array.
For (I = 0; I <Max; ++ I)
{
/* Do something */
}
Replace
For (P = A; P <& A [Max]; ++ P)
{
/* Do something */
}
This method is everywhere in existing C Programs, so the ansic standard stipulates that this method is feasible.

See:
9.3 why be careful with the addresses of those elements behind the array?
9.5 Which of the following is a better way to access elements in an array by using pointers or array names with underlying targets?

9.3 why be careful with the addresses of those elements behind the array?
If your program runs on an ideal computer, that is, it ranges from 00000000 to ffffffff, you can rest assured, but the actual situation is often not that simple.
On some computers, addresses are composed of two parts. The first part refers to the starting point of a memory, that is, the needle (that is, the base address ), the second part is the address offset relative to the starting point of the memory. This address structure is called a segment address structure. subroutine calls are usually implemented by adding an address offset to the stack pointer. The most typical example of a segment-address structure is an Intel 8086-based computer where all MS-DOS programs run (on a Pentium chip-based computer, most MS-DOS programs are also running in a 8086 compatible mode ). Even if it is a well-performing, online address space-based Proteus chip, it also provides a register address change addressing method, that is, using a register to save the pointer to the starting point of a memory, use another register to save the address offset.
If your program uses the segment address structure and the base address stores the array A0 (that is, the base address pointer is the same as & A0 [0]), what problems will this lead? Since the base address cannot be (effectively) changed, and the offset cannot be a negative value, it makes no sense to say that "the element located before A0 [0, the ansic standard clearly stipulates that the behavior of referencing this element is not defined, which is the reason why the method mentioned in 9.1 may not work.
Similarly, if array A (the number of elements is max) is stored at the end of a memory segment, the address & A [Max] is meaningless, if & A [Max] is used in your program, and the compilation program must check whether & A [Max] is valid, then the Compilation Program will inevitably Report that there is not enough memory to store array.
Although the above problems are not encountered when writing a program based on Windows, Unix or Macintosh, the C language is not only designed for these situations, but must be adapted to a variety of environments, for example, with microprocessor control of the brew oven, anti lock brake system, MS-DOS, and so on. Programs Written in strict accordance with the C language standards can be smoothly compiled and can serve any purpose, but sometimes programmers can also moderately deviate from the C language standards, this depends on the specific requirements of programmers, compiler programs, and program users.
See:
9. Does the subscript of the array always start from 0?
9.2 can I use the address of the first element behind the array?

9.4 When passing an array as a parameter to a function, can the sizeof operator tell the size of the Function Array?
No. When an array is used as a function parameter, you cannot pass the array parameter itself to tell the function the size of the array when running the program, because the array parameter of the function is equivalent to a pointer pointing to the first element of the array. This means that the efficiency of passing an array to a function is very high, and it also means that the programmer must use some mechanism to tell the parameter size of the Function Array.
To tell the size of Function Array parameters, we usually use the following two methods:
The first method is to pass the array together with the value indicating the array size to the function. For example, the memcpy () function does this:
Char source [Max], Dest [Max];
/*...*/
Memcpy (DEST, source, max );
The second method is to introduce a rule to end an array. For example, in C language, strings always end with the ASCII character NUL ('\ 0'), while a pointer array always ends with a null pointer. See the following function. Its parameter is
Array of character pointers at the end of the needle. This NULL pointer tells the function when to stop working:
Void printings (char * strings port)
{
Int I;
I = 0;
While (strings [I]! = NULL)
{
Puts (strings [I]);
++ I;
}
}
As mentioned in 9.5, C programmers often use pointers to replace array subscript. Therefore, most C programmers usually write the above functions more concealed:
Void printings (char * strings [])
{
While (* strings)
{
Puts (* strings ++ );
}
}
Although you cannot change the value of an array name, strings is an array parameter, which is equivalent to a pointer. Therefore, you can perform auto-increment operations on it and call puts () perform auto-increment operations on strings. In the preceding example, while (* strings)
It is equivalent
While (* strings! = NULL)
When writing a function document (for example, adding a comment before a function, writing a memo, or writing a design document, it is very important to know how the size of array parameters is written into a function. For example, you can simply write "end with a null pointer" or "the number of numelephants elements in the array elephants" (if you use number 13 in the program to indicate the size of the array, you can write a description like "13 elements in array arr". However, it is not a good programming habit to use exact numbers to indicate the size of the array ).

See:
9.5 Which of the following is a better way to access elements in an array by using pointers or array names with underlying targets?
9.6 can I assign another address to an array name?

9.5 Which of the following is a better way to access elements in an array by using pointers or array names with underlying targets?
Using pointers makes it easier for C compilers to generate high-quality code than using subscripts.
Suppose your program has the following code:

/* X La some type */
X a [Max];
X * P;/* pointer */
X;/* element */
Int I;/* Index */

To review all elements in array A, you can use this loop method (method)
/* Version ()*/
For (I = 0; I <Max; ++ I)
{
X = A [I];
/* Do something with x */
}
You can also use this loop method (method B)
/* Veraion (B )*/
For (P = A; P <& A [Max]; ++ P)
{
X = * P;
/* Do aomething with x */
}
What are the differences between the two methods? The initial conditions of the two methods are the same as those of the incremental operation, and the comparison expressions used as cyclic conditions are the same (this will be further discussed below ). The difference is that "x = A []" and "X = * P", the former must determine the address of a [I, therefore, we need to multiply the size of I and Type X and then add the addresses of the first element in array;
The latter only needs to indirectly reference the pointer p. Indirect reference is fast, but multiplication is slow.
This is a "micro-efficiency" phenomenon, which may have an impact on the overall efficiency of the program, or it may have no impact. For method A, if the operation in the loop body is to add the elements in the array, or just move the elements in the array, most of the time in each loop is consumed by using the array subscript; if the operation in the loop body is an I/O operation or a function call, the time consumed by using the array subscript is negligible.
In some cases, the overhead of multiplication is reduced. For example, when the size of Type X is 1, the multiplication operation can be omitted after optimization (a value multiplied by 1 is still equal to this value ); when the size of Type X is a power of 2 (in this case, Type X is usually the inherent type of the system ), the multiplication operation can be optimized to the Left shift operation (just like a decimal number multiplied by 10 ).
In method B, the calculation of a [Max] is required for each loop. What is the cost? Is this the same price as the calculation of a [I? The answer is different, because & A [Max] remains unchanged in the loop process. Any qualified Compilation Program will calculate & A [Max] only once at the beginning of the loop, and the calculated value will be reused in each subsequent loop.
When the Compiling Program confirms that both A and Max remain unchanged during the loop process, Method B has the same effect as the following code:
/* How the compiler implements version (B )*/
X * temp = & A [Max];/* optimization */
For (P = A; P <temp; ++ P)
{
X = * P;
/* Do something with x */
}

You can also use two methods to traverse array elements, that is, to traverse array elements in descending rather than ascending order. For tasks such as printing array elements in order, the latter two methods have no advantages, but the latter two methods are better than the first two methods for tasks such as adding array elements. The method of traversing array elements in descending order by subscript (method C) is as follows (people usually think that a value and. The cost of comparison is lower than that of comparing a value with a non-zero value:

/* Version (c )*/
For (I = max-1; I> = 0; -- I)

{
X = A [I];
/* Do aomcthing with x */
}

The method of traversing array elements in descending order by pointer (method D) is as follows. The comparison expression as a loop condition is concise:

/* Version (d )*/
For (P = & A [Max-1]; P> = A; -- p)
{
X = * P;
/* Do something with x */
}

Code similar to method D is very common, but it is not absolutely correct, because the condition of loop end is that P is smaller than a, which is sometimes impossible (see 9.3 ).
Generally, people think that "any qualified compilation program that can optimize the code will generate the same code for these four methods", but in fact many compilation programs fail to do this. I have compiled a test program (the size of Type X is not a power of 2, and the operations in the loop body are irrelevant ), compile the program with four very different types of programs. The result shows that method B is always much faster than method A, and sometimes it is twice faster, it can be seen that the effect of using a pointer is very different from that of using a lower mark (one thing is the same, that is, all the four compilation programs have optimized & A [Max] as mentioned above ).
When traversing array elements, what is the difference between descending order and ascending order? For the two compilation programs, the speed of method C and method D is basically the same as that of method A, while that of Method B is obviously the fastest (probably because of its low cost of compared operations, but can it be considered that descending order is slower than ascending order ?);
For the other two types of compilation programs, the speed of method C is basically the same as that of method A (using subscript is slower), but the speed of method D is slightly faster than that of method B.
All in all, when writing a program with good portability and high efficiency, to traverse array elements, using pointers is faster than using subscripts; when using pointers, method B should be used. Although method D can work normally, the code generated by compiling the program in method D may be slower.

It should be added that the above technique is just a subtle optimization, because operations in the loop body usually consume most of the running time, and many c programmers tend to ignore this situation, I hope you will not make the same mistake.

See:
9.2 can I use the address of the first element after the array?
9.3 why be careful with the addresses of those elements behind the array?

9.6 can I assign another address to an array name?
No, although it seems that this can be done in a common special case.
The array name cannot be placed on the left of the value assignment operator (it is not a left value, not a modifiable left value ). An array is an object, and its array name is a pointer to the first element of the object.
If an array uses extern or static, its array name is a constant known during connection. You cannot modify the value of such an array name, just as you cannot change the value of 7.
Assigning values to the array name is unfounded. The meaning of a pointer is "there is an element here, and there may be other elements before and after it". The meaning of an array name is "here is the first element in an array, there is no array element before it, and only the array element after it can be referenced through the array subscript ". Therefore, if a pointer is required, a pointer should be used.
There is a very common special case. In this special case, it seems that you can modify the value of an array name:
Void F (chara [12])
{
+ + A;/* legal! */
}
The secret is that the array parameter of the function is not a real array, but a real pointer. Therefore, the preceding example and the following example are equivalent:
Void F (char *)
{
+ + A;/* certainlylegal */
}
If you want the array name in the above function to be unmodifiable, you can write the above function as follows, but to do this, you must use the pointer Syntax:
Void {(char * const)
{
+ + A;/* illegal */
}
In the above example, parameter A is a left value, but the key word const in front of it indicates that it cannot be modified.

See:
. 4 when passing an array as a parameter to a function, can the sizeof operator tell the size of the Function Array?

9.7 what is the difference between array_name and & array_name?
The former is the pointer to the first element in the array, and the latter is the pointer to the entire array.
Note: when reading this article, I suggest you temporarily put down this book and write a description of the pointer variable pointing to a character array containing Max elements. Tip: use parentheses. I hope you will not be perfunctory, because only in this way can you truly understand the mysteries of the syntax of the complex pointers in C language. The following describes how to obtain a pointer to the entire array.
An array is a type that has three elements: basic type (the type of an array element) and size (except when the array is described as incomplete ), the value of the array (the value of the entire array ). You can use a pointer to point to the value of the entire array:
Char A [Max];/* arrayofmaxcharacters */
Char * P;/* pointer to one character */
/* Pa is declared below */
Pa = & Al
P = A;/* = & A [0] */
After running the above Code, you will find that the print results of P and PA are the same value, that is, P and PA point to the same address. However, P and PA point to different objects.
The following definition does not obtain a pointer to the value of the entire array:
Char * (AP [Max]);
The above definition is the same as the following definition, and their meaning is "AP is an array containing Max character Pointers ";
Char * AP [Max];

9.8 why can't the constants described by const be used to define the initial size of an array?
Not all constants can be used to define the initial size of an array. In C Programs, only the constant expressions in C can be used to define the initial size of an array. However, in C ++, the situation is different.
The value of a constant expression remains unchanged during the program running, and is a value that can be calculated by the compiler. When defining the size of an array, you must use a constant expression. For example, you can use numbers:
Char A [512];
Or use a predefined constant identifier:
# Deprecision Max 512
/*...*/
Char A [Max];
Or use a sizeof expression:
Char A [sizeof (structcacheobject)];
Or use an expression composed of constant expressions:
Char Buf [sizeof (struct cacheobject) * max];
Or use an enumerated constant.
In C, an initialized constint variable is not a constant expression:
Int max = 512;/* not a constant expression in C */
Char buffer [Max];/* notvalid C */
However, in C ++, using the const int variable to define the size of the array is completely legal and is recommended by C ++. Although this increases the burden on C ++ compilation programs (that is, tracking the value of the const int variable), c compilation programs do not have this burden, however, this also frees the C ++ program from relying on the C Preprocessing Program.

See;
Is the subscript of array 9.1 always starting from 0?
9.2 can I use the address of the first element behind the array?

9.9 what are the differences between strings and arrays?
Array elements can be any type, while strings are an extraordinary array that uses a well-known rule to determine its length.
There are two types of languages. One is simply to regard a string as a character array, and the other is to regard a string as an extraordinary type. C belongs to the previous one, but it is complementary that the C string ends with a NUL character. The value of the array is the same as the address of the first element in the array (or pointer to the element). Therefore, a C string is usually equivalent to a character pointer.
The length of an array can be arbitrary. When the array name is used as a function parameter, the function cannot know the size of the array through the array name itself, so some rule must be introduced. For a string, this rule is that the last character of the string is the ASCII character NUL ('\ 0 ').
In C, the literal value of an int type value can be 42 or, the floating point value can be a single-precision or double-precision value such as 4.2el.
Note: In fact, a char literal is another representation of an int literal, but uses an interesting syntax, for example, when both 42 and '*' represent char-type values, they are two identical values. However, in C ++, the situation is different. c ++ has a real char-type literal value and char-type function parameters, and usually it is more careful to distinguish char type and INT type.
, The integer array and character array do not have the literal value. However, if there is no string literal value, it will be difficult to compile the program, so C provides the string literal value. It should be noted that, according to the Convention, the C string always ends with the NUL character, so the literal value of the C string also ends with the NUL character, for example, the length of "Six times nine" is 15 characters (including NUL Terminator), rather than 14 characters that you can see.
There is also a little-known but very useful rule about the string literal value. If there are two adjacent strings in the program, the compiler treats them as a long string literal value, only one NUL Terminator is used. That is to say, "Hello," world "and" Hello, world "are the same, and several strings in the following code can be separated and combined at will:
Char message [] =
"This is an extremely long prompt \ n"
"How long is it? \ N"
"It's so long, \ n"
"It wouldn't fit on one line \ n ";
When defining a string variable, you need to have an array or pointer that can hold the string, and make sure to leave space for the NUL Terminator, for example, there is a problem in the following code:
Char greeting [12];
Strcpy (greeting, "Hello, world");/* trouble */
In the preceding example, greeting can only contain 12 characters, while "Hello, world" can contain 13 characters (including NUL Terminator ), therefore, NUL characters are copied to a location other than greeting, which may destroy some data in the memory space around greetlng. See the following example:
Char greeting [12] = "Hello, world";/* notastring */
In the above example, there is no problem, but greeting is a character array instead of a string. In the above example, there is no space for the NUL Terminator, so greeting does not contain the NUL character. A better way is to write the statement as follows:
Char greeting [] = "Hello, world ";
In this way, the compilation program calculates the amount of space required to accommodate all the content, including NUL characters.
The literal value of a string is an array of characters (char type), rather than a array of character constants (const char type. Although the ansic committee can redefine the literal value of a string as a String constant array, this will suddenly cause huge confusion by failing to compile millions of lines of code. If you try to modify the content in the string literal value, the compile program is
It won't stop you, but you shouldn't. The compiler may choose a memory area that is not allowed to be modified to store the string literal value, for example, Rom or memory area where write operations are prohibited by the memory ing register. However, even if the string literal value is stored in the memory area promised to be modified, the Compilation Program may share them. For example, if you write the following code (and the string literal value is promised to be modified ):
Char * P = "message ";
Char * q = "message ";
P [4] = '\ 0';/* P now points to "mess "*/
Compile the program to make two possible responses. One is to create two independent strings for p and q. In this case, Q is still "message "; one is to create only one string (both p and q point to it). In this case, Q will become "mess ".
Note: Some people refer to this phenomenon as "C's humor". It is precisely because of this humor that the vast majority of C programmers will be troubled by their own programs all day long, so it is rare to be idle.

This article is from www.21shipin.com 21.

C language programming FAQ array _ C Language Programming Tutorial original link: http://www.21shipin.com/html/61368.shtml

 

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.