Here, I will analyze and apply C language structures around the following two questions:
1. What is the function of structures in C language?
2. What are the requirements for memory alignment of structure member variables (key points)
For the explanation of some concepts, I will not bring up the definitions from the C language textbook. Let's sit down and talk slowly.
=== ...
1. What is the function of the structure?
Three months ago, a senior in the teaching and research department encountered this question in an interview at Huawei Nanjing Research Institute. Of course, this is just the most basic question in an interview. If asked, how would you answer?
My understanding is that structures in C language have at least the following three functions:
(1)Organically organizes the attributes of the object.
For example, in the development of STM32 RTC, we need data to represent the date and time, which are usually year, month, day, hour, minute, and second. If we don't use a structure, we need to define 6 variables to represent it. In this way, the data structure of the program is loose, and our data structure is better to be "high cohesion and low coupling". Therefore, it is better to use a structure to represent it, whether in terms of program readability, portability, or maintainability:
typedef struct //Gregorian calendar date and time structure
{
vu16 year;
vu8 month;
vu8 date;
vu8 hour;
vu8 min;
vu8 sec;
}_calendar_obj;
_calendar_obj calendar; //define structure variables
(2) The method of modifying the structure member variables replaces the redefinition of the function (input parameters).
If the structure is good looking because it organizes the properties of an object, then the structure is useful because it is modified instead of redefining the function (input parameters). Let's continue to use the above structure as an example. Suppose I have the following function to display the date and time:
void DsipDateTime( _calendar_obj DateTimeVal)
Then we just need to call DsipDateTime() with a variable of the structure type _calendar_obj as an actual parameter. DsipDateTime() uses the variable of DateTimeVal to display the content. If we don't use the structure, we may need to write a function like this:
void DsipDateTime(vu16 year, vu8 month, vu8 date, vu8 hour, vu8 min, vu8 sec)
Obviously, such parameters are not very impressive, and the data structure management is also cumbersome. If the return value of a function is a data representing date and time, it will be even more complicated. This is only one aspect.
On the other hand, if the user needs to include the day of the week in the data representing the date and time, at this time, if the structure body was not used before, a formal parameter vu8 week should be added to the DsipDateTime() function:
void DsipDateTime( vu16 year,vu8 month,vu8 date,vu8 week,vu8 hour,vu8 min,vu8 sec)
It can be seen that this method of passing parameters is very cumbersome. So one of the benefits of using a structure as a function's entry parameter is
The declaration of the function void DsipDateTime(_calendar_obj DateTimeVal) does not need to be changed, only the member variables of the structure need to be added, and then calendar.week can be processed accordingly in the internal implementation of the function . This is very useful in program modification and maintenance.
typedef struct //Gregorian calendar date and time structure
{
vu16 year;
vu8 month;
vu8 date;
vu8 week;
vu8 hour;
vu8 min;
vu8 sec;
}_calendar_obj;
_calendar_obj calendar; //define structure variables
(3) The memory alignment principle of the structure can improve the CPU's access speed to the memory (trading space for time).
In addition, the address of the structure member variable can be calculated based on the base address (with the offset). Let's first look at the following simple program. The analysis of this program will be detailed in Part 2, Memory Alignment of Structure Member Variables. [page]
}
#includeint main() { struct //Declare the structure char_short_long { char c; short s; long l; }char_short_long; struct //Declare the structure long_short_char { long l; short s; char c; }long_short_char; struct //Declare the structure char_long_short { char c; long l; short s; }char_long_short; printf(" "); printf(" Size of char = %d bytes ",sizeof(char)); printf(" Size of shrot = %d bytes ",sizeof(short)); printf("Size of long = %d bytes ",sizeof(long)); printf(" "); //char_short_long printf(" Size of char_short_long = %d bytes ",sizeof(char_short_long)); printf("Addr of char_short_long.c = 0x%p (decimal: %d) ",&char_short_long.c,&char_short_long.c); printf("Addr of char_short_long.s = 0x%p (decimal: %d) ",&char_short_long.s,&char_short_long.s); printf("Addr of char_short_long.l = 0x%p (decimal: %d) ",&char_short_long.l,&char_short_long.l); printf(" "); printf(" "); //long_short_char printf("Size of long_short_char = %d bytes ",sizeof(long_short_char)); printf("Addr of long_short_char.l = 0x%p (decimal: %d) ",&long_short_char.l,&long_short_char.l); printf("Addr of long_short_char.s = 0x%p (decimal: %d) ",&long_short_char.s,&long_short_char.s); printf("Addr of long_short_char.c = 0x%p (decimal: %d) ",&long_short_char.c,&long_short_char.c); printf(" "); printf(" "); //char_long_short printf(" Size of char_long_short = %d bytes ",sizeof(char_long_short)); printf("Addr of char_long_short.c = 0x%p (decimal: %d) ",&char_long_short.c,&char_long_short.c); printf("Addr of char_long_short.l = 0x%p (decimal: %d) ",&char_long_short.l,&char_long_short.l); printf("Addr of char_long_short.s = 0x%p (decimal: %d) ",&char_long_short.s,&char_long_short.s); printf(" "); return 0; }
The program's running results are as follows (note: the data in brackets is the decimal form of the member variable's address):
2. Memory alignment of structure member variables
First, let's analyze the running results of the above program. The first three lines show that in my program, char type occupies 1 byte, short type occupies 2 bytes, and long type occupies 4 bytes. char_short_long, long_short_char, and char_long_short are three structures with the same members but different member variable order. And from the running results of the program,
Size of char_short_long = 8 bytes
Size of long_short_char = 8 bytes
Size of char_long_short = 12 bytes //4 bytes larger than the first two cases!
And, also note that 1 byte (char) + 2 bytes (short) + 4 bytes (long) = 7 bytes, not 8 bytes.
Therefore, the order in which the structure member variables are placed affects the size of the memory space occupied by the structure. The size of the memory occupied by a structure variable is not necessarily equal to the sum of the space occupied by its member variables. If a user program or operating system (such as uC/OS-II) contains a large number of structure variables, this memory usage must be optimized, that is, the order of the member variables inside the structure is particular.
How are structure member variables stored?
Here, I will not keep you in suspense and give the following conclusion directly. In the absence of #pragma pack macro:
Principle 1: The first data member of a structure (struct or union) is placed at offset 0, and each subsequent data member should be stored starting at an integer multiple of the size of the member (for example, if int is 4 bytes on a 32-bit machine, it should be stored starting at an address that is an integer multiple of 4).
Principle 2 The total size of a structure, that is, the result of sizeof, must be an integer multiple of its largest internal member, and any shortfall must be made up.
* Principle 3 When a structure is used as a member, the structure members should be stored starting from an address that is an integer multiple of the size of the largest element inside it. (When struct a contains struct b, and b contains elements such as char, int, and double, then b should be stored starting from an address that is an integer multiple of 8, because sizeof(double) = 8 bytes)
Here, we analyze it in combination with the above procedures (not discussing principle 3 for the time being).
Let's first look at the two structures char_short_long and long_short_char. From the addresses of their member variables, we can see that these two structures comply with principles 1 and 2. Note that in the address of the member variable of char_short_long, the address of char_short_long.s is 1244994, which means that 1244993 is "empty" and is just a "placeholder"! [page]
Let's look at the char_long_short structure. The address distribution of char_long_short is as follows:
Member variables |
Member variable hexadecimal address |
Member variable decimal address |
char_long_short.c |
0x0012FF2C |
1244972 |
char_long_short.l |
0x0012FF30 |
1244976 |
char_long_short.s |
0x0012FF34 |
1244980 |
As you can see, the memory distribution diagram is as follows, a total of 12 bytes:
address |
1244972 |
1244973 |
1244974 |
1244975 |
1244976 |
1244977 |
1244978 |
1244979 |
1244980 |
1244981 |
1244982 |
1244983 |
member |
.c |
|
|
|
.l |
.s |
|
|
First, 1244972 is divisible by 1, so there is no problem placing char_long_short.c at 1244972 (in fact, as for the char type member variable itself, there is no problem placing it at any address unit). According to principle 1, none of the subsequent 1244973~1244975 is divisible by 4 (because sizeof(long)=4bytes), and 1244976 is divisible by 4, so char_long_short.l should be placed at 1244976. Similarly, the last .s (sizeof(short)=2 bytes) should be placed at 1244980.
Is this the end? No, there is still Principle 2. According to the requirements of Principle 2, the space occupied by the char_long_short structure should be an integer multiple of the size of its largest member variable. If we stop here, the memory space occupied by char_long_short is 1244972~1244981, a total of 10 bytes, which does not meet Principle 2. Therefore, we must fill in 2 bytes (1244982~1244983) at the end.
At this point, the memory layout of a structure is completed.
Let's follow the above principles to verify whether this analysis is correct. According to the above analysis, address units 1244973, 1244974, 1244975, 1244982, 1244983 are all empty (at least char_long_short is not used, it is just a "placeholder"). If our analysis is correct, then defining such a structure should also occupy 12 bytes of memory:
struct //Declare the structure char_long_short_new
{
char c;
char add1; //Fill in space
char add2; //Fill in space
char add3; //Fill in space
long l;
short s;
char add4; //Fill in space
char add5; //Fill in space
}char_long_short_new;
It can be seen that our analysis is correct. As for principle 3, you can verify it by programming yourself , so I will not discuss it here.
Therefore, whether you are in VC6.0, Keil C51, or Keil MDK, when you need to define a structure, as long as you pay a little attention to the memory alignment of the structure member variables, you can save MCU RAM to a large extent. This is not only applied to actual programming , but also common in written tests and interviews of many large companies, such as IBM, Microsoft, Baidu, and Huawei.
Download the complete program code for this example: http://www.51hei.com/f/cjgt.rar
Previous article:Advantages of PLC and MCU and learning precautions
Next article:MEGA16 MCU timer (16-bit) source code
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- Rambus Launches Industry's First HBM 4 Controller IP: What Are the Technical Details Behind It?
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- [STM32WB55 Review] 4# Use of STM32WB development board STM32CubeWB
- How to manually add a network to a PCB in Altium
- How do you learn about the latest products and technologies in the industry? Take power supply as an example:
- EEWORLD University ---- Embedded Operating System
- EEWORLD University ---- Microchip Security Series 9: Firmware Authentication Using TrustFLEX Secure Element
- msp430f149 external interrupt problem
- Share: How to achieve single-layer PCB layout and improve EMI?
- FPGA_Journey_Flowing Light.pdf
- 28335 Procedure
- TI Precision Labs - Motor Drivers