Talking about C language structure

Publisher:TranquilSilenceLatest update time:2015-05-06 Source: 51hei Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
I believe everyone is familiar with structures. Here, I share my summary of my research and study of C language structures. If you find that there is something you have not mastered before in this summary, then this article is of some value. Of course, my level is limited, and if you find any shortcomings, please point them out. I put the code file test.c below.

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]

}

 

#include 
 
int 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;

 

The results are as follows:

 

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

Reference address:Talking about C language structure

Previous article:Advantages of PLC and MCU and learning precautions
Next article:MEGA16 MCU timer (16-bit) source code

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号