Chapter Two focuses on programming technology, with Section 2.2.1 discussing memory alignment and Section 2.2.2 exploring basic data types. We are aware that arrays and pointers group data of the same type in an orderly fashion, but there are instances when bundling different types of data together as a whole becomes essential for simplifying programming tasks. In C programming, such collections are referred to as structures.
Let’s delve deeper into Section 2.2.1 Memory Alignment. Even though variables ultimately reside at specific memory addresses, they must adhere to memory alignment rules. These rules serve two main purposes: platform compatibility and performance optimization. Certain hardware platforms, especially those in embedded systems, can only access data at properly aligned addresses; otherwise, hardware exceptions occur. Furthermore, memory alignment enhances performance since accessing variables stored in aligned memory requires only a single memory access, whereas unaligned memory may necessitate multiple accesses.
In a 32-bit microprocessor, memory is accessed in chunks of 32 bits, or four bytes at a time. For instance, consider 16 bytes of memory ranging from address 0x0 to 0xF. Instead of viewing this as individual bytes, it is grouped into four blocks of four bytes each, as illustrated in Figure 2.4.
[Insert Figure 2.4]
From this diagram, we observe that only addresses that are multiples of four (0x0, 0x4, 0x8, 0xC) allow fetching of four bytes in one operation. Attempting to fetch four bytes from an arbitrary address will fail. Suppose a 4-byte integer is stored starting at address 0. The situation is depicted in Figure 2.5.
[Insert Figure 2.5]
Here, the CPU can retrieve the integer in a single memory access since the data resides entirely within block 0. However, if the integer is stored starting at address 1, as shown in Figure 2.6, the CPU would need two memory accesses to retrieve the data.
[Insert Figure 2.6]
This inefficiency arises because the data spans two blocks—block 0 and block 1. The CPU first retrieves three bytes from block 0, then accesses block 1 to fetch the remaining byte. Combining these bytes forms the complete integer. Clearly, unaligned memory storage significantly impacts CPU efficiency. Moreover, certain processors may crash when encountering improperly aligned data due to hardware limitations.
The rules governing memory alignment are as follows:
1. The starting address of each member variable within a structure must be an integer multiple of the smaller value between the “alignment factor†and the “actual length of the variable.†For example, if a variable requires 4-byte alignment, its starting address must be a multiple of 4, such as 0x0, 0x4, 0x8, 0xC, etc.
2. After aligning each data member, the structure itself must also be aligned. This means the total size of the structure should be an integer multiple of the smaller value between the “alignment factor†and the “maximum data member length.â€
Generally, the alignment factor corresponds to the word length of the microprocessor. For instance, a 32-bit microprocessor typically has an alignment factor of 4 bytes. The length of a variable depends on its type. The method for determining the length of a type is as follows:
[Insert image]
Running this program outputs: 1, 4, 4, 4, 8. Assuming a 32-bit microprocessor with an alignment factor of 4, the structure variable data is defined as follows:
[Insert image]
Each member of the structure starts from the first address of the structure (guaranteed by the compiler to meet memory alignment requirements, assuming zero), and the members are stored in the order they are defined. Refer to Table 2.1 for details.
[Insert Table 2.1]
The actual storage locations are represented by [x, y], where x is the starting address and y is the ending address. If x equals y, it is simply denoted by [x]. For instance, consider member b, which has a length of 2. Since this length is smaller than the alignment factor, its address must be a multiple of 2. Given that address 0 is already occupied by member a, only adjacent memory that satisfies the requirements can be utilized. Thus, [2, 3] stores member b, while [1] remains unused because it does not meet the requirements for storing member b. For an array member c, it cannot be treated as a whole; rather, it should be viewed as two separate members, c[0] and c[1]. Consequently, the actual storage location is [0, 24], and memory spaces 1, 6, 7, 17, 18, 19 are discarded.
After all members are stored, the structure itself must be aligned. This means the size of the structure should also be an integer multiple of the aligned bytes. The number of aligned bytes is determined by the longer member and the smaller value of the “alignment factor.†Here, the longest member is d, which is of double type and has a length of 8, exceeding the alignment factor. Thus, the structure itself is also aligned to 4 bytes, and the space occupied by the structure must be a multiple of 4. Although the current storage location is [0, 24], which occupies only 25 bytes, it must satisfy the requirement of being a multiple of 4. Hence, the structure actually occupies 28 bytes, or [0, 27]. Let’s verify the size of the structure footprint:
[Insert image]
Though the total length of all members is 19 bytes, the structure actually occupies 28 bytes, leaving an extra 9 bytes for memory alignment. These unused spaces are divided into four segments: [1], [6, 7], [17, 19], [25, 27]. Observing Table 2.1, the unused spaces are mostly located before the char-type data. Since char-type data only occupies one byte, it often leads to unused space not being utilized by longer data types.
To minimize memory wastage, members with the smallest lengths should be placed after the char-type data. Therefore, when defining a structure, each member should be arranged in ascending order of length. The definition of the example structure is as follows:
[Insert image]
Similarly, each member is stored sequentially, as shown in Table 2.2.
[Insert Table 2.2]
All members occupy [0, 19], with the space at address 5 being unused. Since the size of the structure is 20 bytes, it is already a multiple of 4, requiring no additional processing. The structure only wastes 1 byte of space, achieving a usage rate of 95%. By optimizing the order in which structure members are defined, memory wastage can be significantly reduced while still adhering to memory alignment requirements.
Now moving to Section 2.2.2 Basic Data Types. One important aspect is range value checking. The rangeCheck() function requires three int parameters: value, min, and max. It returns true if the value falls within the specified range and false otherwise. Here's the implementation of the rangeCheck() function:
[Insert Listing 2.10]
From a coding perspective, clean code is achieved through simplicity and clarity. A good function name, such as rangeCheck, is highly valuable as it clearly conveys the function’s purpose. Shorter functions are preferable as they tend to have more focused functionality, making them easier to name descriptively. While longer names are acceptable if descriptive, overly short or cryptic names should be avoided. Descriptive names help clarify the design intent and often lead to better refactoring opportunities.
Regarding types and variables, thanks to structures, the min and max parameters of rangeCheck() can be encapsulated into a structure, reducing one parameter and making handling easier. Here's how a structure consisting of two int variables is defined:
[Insert image]
This defines a structure named range, which not only holds actual data but also outlines how the structure stores it. range becomes the type of the structure if we precede the struct definition with a typedef:
[Insert image]
At this point, range is equivalent to struct _Range. It is customary to capitalize the first letter of type names and lowercase the first letter of variable names. With the Range type, you can define both a range variable of Range type and a pointer variable pRange that points to the Range * type. You can also omit the type name _Range:
[Insert image]
Note that a structure has dual meanings: one as a “structure layout,†which informs the compiler how to represent the data but does not allocate space for it. The next step is to create a structure variable, which involves defining it as follows:
[Insert image]
The compiler creates a structure variable named range and allocates space for it: an int variable min and an int variable max, collectively referred to as range.
Initialization is another crucial aspect. Assuming the valid range of value is 0~9, you can use the macro named newRangeCheck to easily initialize the structure:
[Insert image]
Usage is straightforward:
[Insert image]
The macro expands as follows:
[Insert image]
Which is equivalent to:
[Insert image]
The effect of .min and .max is similar to using subscripts for the Range structure. Though Range is a struct, both range.min and range.max are variables of type int, so they can be used like other int variables, for example, &(range.min).
Thus, if you initialize a static storage-period structure, the values in the initialization list must be constant expressions. In the case of an automatic storage period, the values in the initialization list may not be constants.
Next, let's discuss interfaces and implementations. Passing structure members is straightforward if the member is a single-value data type such as int, char, float, double, or pointer. The implementation of rangeCheck() is detailed in Listing 2.11.
[Insert Listing 2.11]
Its call form is as follows:
[Insert image]
rangeCheck() neither knows nor cares whether the argument is a member of a struct. It only requires the incoming data to be of type int. If you need to modify the value of a member in the calling function within the called function, you must pass the member’s address.
Passing a structure as a whole is more complex than passing a single value. Standard C also allows structures to be used as parameters. The implementation of rangeCheck() is detailed in Listing 2.12.
[Insert Listing 2.12]
Its call form is as follows:
[Insert image]
While this method yields the correct result, it is inefficient because the C-language parameter addressing method requires copying the parameter to the function. If the structure contains a large array, such as 128 bytes, passing it as a parameter necessitates copying the entire array to the stack, which is discarded afterward.
An alternative approach is passing the address of the structure. Consider a set of data stored in an array of structure members:
[Insert image]
To find the maximum value in the array, you can pass the address of the structure (int *) &st as an argument to the formal parameter of iMax(). See Listing 2.13 for details.
[Insert Listing 2.13]
For a range value verifier, define a pointer variable pRange pointing to the structure, and initialize, assign, and manipulate it similarly to an ordinary pointer variable:
[Insert image]
Unlike arrays, the structure name is not the address of the structure, so the & operator is added before the structure name. Thus, pRange is a pointer variable pointing to the range variable of the Range structure. Although the types of pRange, &range, and &range.min are different, their values are equal, and the following relationship holds:
[Insert image]
Since the . operator has a higher precedence than the * operator, parentheses must be used. Here, we focus on understanding that pRange is a pointer, and pRange->min refers to the first member of the structure, making pRange->min a variable of type int. The implementation of rangeCheck() is shown in Listing 2.14.
[Insert Listing 2.14]
rangeCheck() uses the pointer pRange pointing to Range as its argument, passing the address &range to the function, causing the pointer pRange to point to range, and then retrieving the values of range.min and range.max with the -> operator. Note that you must use the & operator to get the address of the structure, which differs from the array name. The structure name is merely an alias for its address.
Its call form is as follows:
[Insert image]
Finally, calling with a function pointer. If you need to add a parity checker to check the value's parity, the data structure is as follows:
[Insert image]
The implementation of the oddEvenCheck() function is detailed in Listing 2.15.
[Insert Listing 2.15]
When the system requires multiple validators, the caller decides which function to call based on the actual situation at runtime. According to the dependency inversion principle, the best approach is to use function pointers to isolate changes. Regardless of the validator, the common processing part is the legality judgment of the value. This is abstracted into a module, with the variable values and check parameters handled by externally passed parameters. Since the various validators are of different types, you must use "void *pData" as a formal parameter to accept any type of data, generalizing Range *pRange and OddEven *pOddEven to void *pData. The Validate type is defined as follows:
[Insert image]
Where pData is a pointer to any validator parameter, value is the value to be verified, and the interface of the universal validator is shown in Listing 2.16.
[Insert Listing 2.16]
Taking the range value verifier as an example, the call form is as follows:
[Insert image]
The function passed to the function this time is a pointer to the structure. A pointer is much smaller than the whole structure, making it far more efficient to push it onto the stack. The implementation of the validator interface is shown in Listing 2.17.
[Insert Listing 2.17]
Since the types of pRange, pOddEven, and pData are different, you need to cast a type to pData to reference the members of the corresponding structure. Note that the author does not provide the complete code here, please supplement it.
VCM Parts
VCM (Voice Coil Motor), the Voice Coil Motor in electronics, is a kind of Motor. Because the principle and loudspeaker similar, so called voice coil motor, with high frequency, high precision characteristics.
Its main principle is in a permanent magnetic field, by changing the motor coil DC current size, to control the stretching position of the spring, so as to drive up and down movement.
VCM is widely used in mobile phone cameras to realize the function of autofocus. Through VCM, the position of the lens can be adjusted to present clear images.
VCM is include of Shield Case, Frame, F.Spacer, F.Spring, Yoke, Magnet, Coil, Carrier, B.Spacer, B.Spring, Base.
Components Vcm Gasket,Components Vcm Gaskets,Camera Vcm Terminals,No Notch Terminal
SHAOXING HUALI ELECTRONICS CO., LTD. , https://www.cnsxhuali.com