Chapter two focuses on programming technology, specifically sections 2.2.1 on memory alignment and 2.2.2 on basic data types. We know that arrays and pointers group ordered data of the same type, but there are times when we need to bundle different types of data together as a unified entity to simplify programming. In the C language, such a collection of data is referred to as a structure.
Moving forward, let’s delve into memory alignment, which plays a crucial role in how variables are stored in memory. Even though all variables are ultimately stored at specific memory addresses, the allocated memory space must adhere to memory alignment requirements. These requirements serve two main purposes: platform compatibility and performance optimization.
Firstly, platform compatibility ensures that hardware can access data efficiently. Not all hardware platforms, especially those in embedded systems, can access data at arbitrary addresses. Some hardware can only process data stored at aligned addresses, otherwise it may lead to hardware exceptions. Secondly, performance optimization reduces the number of memory accesses required. If data is stored in an unaligned memory space, the processor might need two memory accesses to retrieve a variable, whereas aligned memory access requires only one.
For instance, in a 32-bit microprocessor, the processor reads or writes data in 32-bit chunks, meaning four bytes at a time. Consider a 16-byte memory segment starting from address 0x0. Instead of treating it as 16 individual bytes, it's grouped into four blocks of four bytes each, as illustrated in Figure 2.4.
Figure 2.4 shows a schematic representation of memory space. Clearly, data can only be fetched in blocks of four bytes starting from addresses like 0x0, 0x4, 0x8, and 0xC. Attempting to read four bytes from an arbitrary address would be inefficient. Suppose a 4-byte integer is stored in the memory starting at address 0. As depicted in Figure 2.5, the CPU only needs one memory access to read or write this data.
However, if the integer is stored starting at address 1, as shown in Figure 2.6, the data spans two blocks, requiring two memory accesses. First, three bytes are retrieved from block 0, followed by retrieving one byte from block 1. These bytes are then combined into a complete integer via operations. This demonstrates that unaligned memory storage significantly reduces CPU efficiency. In some processors, this might cause a system exception, leading to a crash. Memory alignment rules are generally as follows:
1. The first address of each member variable in a structure must be a 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, the first address of its memory space must be a multiple of 4, such as 0x0, 0x4, 0x8, etc.
2. After aligning each data member within a structure, the structure itself must also be aligned. This means the total size of the structure should be a multiple of the smaller value between the “alignment factor†and the “maximum length of the data members.â€
Generally, the alignment factor corresponds to the word length of the microprocessor. For a 32-bit microprocessor, the alignment factor is typically 4 bytes. The actual length of a variable depends on its type, calculated as follows:
[Insert image showing type lengths]
Running this program outputs: 1, 4, 4, 4, 8. Assuming a 32-bit microprocessor with an alignment factor of 4, consider the following structure variable definition:
[Insert image showing structure definition]
Each member of the structure starts from the first address of the structure (ensured by the compiler to meet memory alignment requirements, assuming zero), and members are stored in the order they are defined. Details are outlined in Table 2.1.
Table 2.1 shows how members are sequentially stored.
[Insert image showing table]
The actual storage location is represented by [x, y], where x is the starting address and y is the ending address. If x equals y, it is simply represented by [x]. For instance, member b has a length of 2, which is smaller than the alignment factor. Therefore, if aligned by 2 bytes, its address must be a multiple of 2. If address 0 is already occupied by member a, only neighboring memory that satisfies the requirements can be used. Space [2, 3] stores member b, while space [1] is deprecated since it does not meet the requirements for storing member b. Array member c cannot be treated as a single unit; rather, it should be viewed as two separate members, c[0] and c[1]. Hence, the actual storage location is [0, 24], with spaces 1, 6, 7, 17, 18, 19 being discarded.
Once all members are stored, the structure itself must be aligned. That is, the size of the structure should also be a multiple of the number of aligned bytes. The number of aligned bytes is the maximum length of the members and the smaller value of the “alignment factor.†Here, the member with the longest length is the double type d, which has a length of 8, greater than the alignment factor. Thus, the structure itself is also aligned in 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. Therefore, the actual space occupied by the structure is [0, 27], totaling 28 bytes. This can be verified as follows:
[Insert image showing verification]
While the total length of all members is 19 bytes, the structure actually occupies 28 bytes, leaving 9 bytes of wasted space due to memory alignment: [1], [6, 7], [17, 19], [25, 27]. Observing Table 2.1, the front of these wasted spaces contains char type data. Since char type data only occupies one byte, it often leads to unused space for data of longer lengths.
To minimize memory wastage, members with the smallest length should be placed after the char type data. In other words, when defining a structure, members should be arranged in order of increasing length. The structure definition could be as follows:
[Insert image showing optimized structure definition]
Similarly, members are stored sequentially, as shown in Table 2.2.
Table 2.2 shows how members are sequentially stored.
[Insert image showing table]
All members are stored in [0, 19], with address 5 in the middle being deprecated. Since the size of the structure is 20 bytes, it is already a multiple of 4, requiring no further 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, let’s explore basic data types. The rangeCheck() function checks if a value falls within a specified range. If min ≤ value ≤ max, the function returns true; otherwise, it returns false. Here’s an example:
[Insert image showing rangeCheck() function]
Code cleanliness is essential. A descriptive name like rangeCheck clearly communicates the function’s purpose, underscoring the importance of good naming conventions. Shorter functions are easier to maintain and often benefit from more intuitive names. Parameters should be minimized for clarity, with the ideal number being zero, followed by one, then two. Avoid functions with more than three parameters unless absolutely necessary, as they complicate testing and comprehension.
Structures allow us to encapsulate parameters, reducing the number of function arguments while improving readability. For example, the rangeCheck() function can transfer min and max parameters via a structure:
[Insert image showing structure definition]
This defines a structure consisting of two int variables. A typedef can simplify this to:
[Insert image showing typedef]
The structure now becomes a type, allowing the creation of variables like range and pointers like pRange. Initialization can be done using macros:
[Insert image showing macro usage]
Initialization lists must contain constant expressions for static storage, but non-constant values can be used for automatic storage.
Passing structure members as parameters is straightforward for single-value data types. However, passing entire structures is less efficient due to copying overhead. Instead, passing the address of the structure is recommended. This approach is particularly useful when dealing with arrays of structures:
[Insert image showing array of structures]
The iMax() function can find the maximum value by passing the address of the structure:
[Insert image showing iMax() function]
Pointers to structures behave similarly to regular pointers, with careful handling of dereferencing:
[Insert image showing pointer operations]
Finally, function pointers can be used to implement modular validation logic. By isolating changes through function pointers, we can dynamically choose validators at runtime:
[Insert image showing function pointer usage]
In conclusion, understanding memory alignment and basic data types is fundamental to effective C programming. Proper structuring and parameter management enhance code readability and performance.
We custom Etching Cutting Die, Etching Metal Crafts, Etching Eyeglass Frames, Etching Plate Heat Exchanger Sheet with drawings provided by customers. We are equipped with professional metal etching equipment and exposure development equipment. The raw material we more use for these products is stainless-steel, brass, titanium and titanium-palladium. We can guarantee that our half etching plate heat exchanger sheet have straight surface line, and have no burr, high product accuracy.
Etching Cutting Die,Etching Metal Crafts,Etching Eyeglass Frames,Etching Plate Heat Exchanger Sheet
SHAOXING HUALI ELECTRONICS CO., LTD. , https://www.cnsxhuali.com