Current location - Plastic Surgery and Aesthetics Network - Plastic surgery and medical aesthetics - Macros define why there is an offset when defining data.
Macros define why there is an offset when defining data.
#define OFFSETOF(type,field)((size _ t)& amp; ((((type *)0)-& gt; Field))

(type *)0: Take the address of 0 as the pointer of type.

((type *)0)-& gt; Field: A variable corresponding to a field.

& amp((type *)0)-& gt; Field: the address of the variable is actually equal to the offset of the field from address 0.

(size _ t)& amp; ((((type *)0)-& gt; Field): Convert the address (offset) into size_t data.

The ANSI C standard allows any constant with a value of 0 to be forcibly converted into any type of pointer, and the conversion result is a null pointer, so the result of ((s*)0) is a null pointer of type S *. It is illegal to use this null pointer to access members of S, but&(((s *) 0)-> The intention of m) is not to access the contents of the s field, but only to calculate the address of the m field when the first address of the structure instance is ((s*)0). The intelligent compiler does not generate the code to access M at all, but only calculates this (constant) address according to the memory layout of S and the first address of the structure instance at compile time, thus completely avoiding the problem of accessing memory through a null pointer.

note:

1.

Someone said this:

#define OFFSETOF(type,field) ((size_t) \

((char *)& amp; ((type *)0)-& gt; field - (char *)(type *)0))

I think the effect is the same, the extra part is 0 address, and the offset is after subtraction.

2.

Why increase the size_t?

First of all, what is the definition of size_t? The answer can be found in the stddef.h file.

Typedef unsigned int size _ t;; /* Mine is a 32-bit machine */

As you can see, the offset is converted to an unsigned integer. In fact, the address of a 32-bit machine is an unsigned 32-bit integer. Generally speaking, there is no problem without size_t type conversion (this can be proved by later experiments). In my opinion, only if the offset is large enough, greater than 0x80000000, will it have an impact, because the highest bit of the offset is 1, and the machine defaults to a negative number. The macro definition of OFFSETOF above seems to explain this problem better, because this macro definition is a difference, and the highest bit is 1, which must be a negative number. Use printf ("%d ",&; var); The address of the print variable is negative. This is just my opinion. Few people on the Internet have analyzed why the mandatory type conversion of size_t should be added. Because the system has restrictions on the size of the array length, it is impossible to get the data through experiments.

Problem of inserting array length (reference):

There is no limit in theory, but the general configuration of the kernel allows each process to have limited memory space, and the system call function getrlimit (int resource, structrlimit * rlim) can be used.

Gets the resource limit of the system. The resource limitation of the system is divided into software limitation and hardware limitation, and the maximum value of software limitation cannot exceed hardware limitation. The storage space obtained by array statically is allocated in the stack, so you can know the answer as long as you know the limit of the stack. It can be obtained by using the following code:

struct rlimit resource _ limit

getrlimit(RLIMIT _ STACK & amp; Resources _ constraints);

printf(" STACK:soft _ limit-% LD hard _ limit-% LD \ n ",resource_limit.rlim_cur,resource _ limit . rlim _ max);

Allocate a lot of arrays. If the system cannot find such a continuous storage space, the system will generate a SIGSEGV signal. At this time, the function int sigaltstack (conststack _ t * SS, stack _ t * OSS) is called to process this signal. Sigaltstack stores the signal SIGSEGV in the standby stack structure ss, and the kernel will check the signal before the process runs.

3.

Therefore, the number of bytes occupied by a field in the structure is very simple.

#define FIELD_SIZE(type,FIELD)sizeof((type *)0)-& gt; Field)

4.

In fact, the system provides the same macro definition. In the file stddef.h:

In embedded systems, different developers, processors and compilers with different architectures have different definitions of offsetof:

/* Keil 805 1 */

# Define the offset (s, m) (size _ t) (((s *) 0)->; m)

/* Microsoft x86 */

#define offsetof(s, m) (size_t) (unsigned long integer)&(((s *) 0)-> m)

/* Motorola Cool Fire */

#define offsetof(s,member)((size _ t)((char *)& amp; ((s *)0)-& gt; member-(char *)0))

/* GNU GCC 4.0.2 */

#define offsetof(TYPE,MEMBER) __builtin_offsetof (TYPE,MEMBER)

Although the definition forms are different, the function is to return the offset of members in the data structure, in order to improve the portability of the code.

5.

Although offsetof is also suitable for union structure, it cannot be used to calculate the offset of bit field members in data structure.

Typedef structure

{

Unsigned int a: 3;

Unsigned int b:13;

Unsigned int c:16;

} foo

Use offset(foo, a) to calculate the offset of a in foo, and the compiler will report an error.

6. Application (citation)

Offsetof and EEPROM

Many of us may have used some non-volatile memory, such as the common EEPROM. We often use them to store some system configuration parameters and equipment information. In all EEPROM, serial access accounts for the majority. Generally speaking, access to the serial port is carried out in bytes, which makes us inevitably design the following interfaces to access EEPROM information:

/* Read nBytes from EEPROM offset to RAM address dest*/

ee_rd(uint 16_t offset,uint 16_t nBytes,uint 8 _ t * dest);

However, this interface must know the offset and the number of bytes read nBytes. Maybe you will solve this problem in the following ways:

Define a data structure and a pointer to the data structure, and initialize the pointer to the starting address EEPROM_BASE.

# defineeprom _ base0x0000000/* Starting address of configuration information */

Typedef structure

{

int I;

Floating f;

char c;

} EEPROM

EEPROM * const pEE = EEPROM_BASE

ee _ rd(& amp; (pee-> f),sizeof(pEE-& gt; f)、dest);

Yes, this method can really access the information of the specified address. However, this method also has the following problems:

A. code maintainers can easily mistake the ee_rd interface for EEPROM data structure.

B. When you write some code that you feel good about, the compiler does not report an error, such as Pee->; F = 3.2, you may not think that disaster will come.

C. This interface can't reflect the hardware features implied by EEPROM.

At this point, some people may think of using offsetof to solve this problem:

#define offsetof(type,f) ((size_t) \

((char *)& amp; ((type *)0)-& gt; f - (char *)(type *)0))

Typedef structure

{

int I;

Floating f;

char c;

} EEPROM

ee_rd(offsetof(EEPROM,f),4,dest);

It will be better if the compiler calculates nBytes instead of giving it to us. At this time, someone will definitely mention sizeof immediately. But how to use it? We can't use sizeof(EEPROM.f) to calculate nBytes, can we? ! Because EEPROM is a data type, not an object, there is no way to operate the F field.

/* Similar to the definition of offsetof */

#define SIZEOF(s,m)((size _ t)SIZEOF((s *)0)-& gt; m))

ee_rd(offsetof(EEPROM,f),SIZEOF(EEPROM,f),& ampdest);

In fact, it can be reduced to the following final form:

# define EE_RD(M, D) ee_rd(offsetof(EEPROM, m), SIZEOF(EEPROM, m), d).

EE _ RD(f & amp; dest);

Haha, so we only need to pass two parameters, regardless of how much data we should read from there.

Some people will say that this simplification is based on 0x0000000 of EEPROM_BASE, and may ask, what if the configuration information does not start from address 0?

In fact, it can be solved by the following methods.

# define EEPROM_BASE 0x00000a 10.

Typedef structure

{

Charging pad [EEPROM _ base]; /* Set the first EEPROM_BASE byte of the data structure to "null" */

int I;

Floating f;

char c;

} EEPROM

It is wonderful to simplify the serial access of EEPROM with offsetof. Here's another good example. In embedded applications, we often map some I/O registers to memory address space for access. This mapping makes the original complex register access as convenient as accessing ordinary RAM addresses. In this way, the PowerPC 8250 accesses the registers of the external ROM controller. All registers of the ROM controller are mapped to a segment of memory offset by 0x60000(ROMCONOffset) bytes from the I/O register space base address 0x 10000000(IO_BASE). Each register occupies 4 bytes and has a corresponding data structure. For example, the register that controls the working state of the ROM controller corresponds to the data structure ROMCON_ROM_CONTROL, and the register that configures PCI bus A corresponds to the data structure Romcon _ Config _ A. Let's take a look at the definitions of these data structures first:

#define IO_BASE 0x 10000000

# define ROMCONOffset 0x60000

Typedef unsigned int NW _ UINT32

typedef struct _ROMCON_CONFIG_A {

labour union

Structure {

uint 32 pad 4:2 1; /* Not used */

uint 32 pad 3:2; /* Reserved */

uint 32 pad 2:5; /* Not used */

uint 32 enable PCIA: 1;

uint 32 pad 1: 1; /* Reserved */

uint 32 enable boot: 1;

uint 32 enable CPU: 1; /* Enable cpu bits */

} nlstruct

Structure {

UINT32 ConfigA

} nlstruct4

} nlunion

} ROMCON_CONFIG_A,* PROMCON _ CONFIG _ A;

typedef struct _ ROMCON _ ROM _ CONTROL {

labour union

Structure {

uint 32 transfer complete: 1;

uint 32 pad 3: 1; /* Not used */

uint 32 bond pad 3 to 2:2;

uint 32 Advance:3;

uint 32 VersaPortDisable: 1;

uint 32 pad 2: 1; /* Not used */

uint 32 FastClks: 1;

uint 32 pad 1:7; /* Not used */

uint 32 CsToFinClks:2;

uint 32 OeToCsClks:2;

uint 32 DataToOeClks:2;

uint 32 OeToDataClks:3;

uint 32 CsToOeClks:2;

uint 32 AddrToCsClks:2;

uint 32 ale width:2;

} nlstruct

Structure {

UINT32 RomControl

} nlstruct4

} nlunion

} ROMCON_ROM_CONTROL,* PROMCON _ ROM _ CONTROL

Typedef structure

{

ROMCON _ CONFIG _ A ConfigA

ROMCON _ CONFIG _ B ConfigB

ROMCON _ ROM _ CONTROL RomControl

...

}ROMCON,* PROMCON

-& lt; -IO_BASE:0x 1000000

| | | | | | | ...

-

| | | | | | | ...

...

-& lt; -ROMCONOffset(ROMCON):0x60000

| | | | | | | ...

-& lt; -ROMCON_ROM_CONTROL

...

-

So how to access the register corresponding to ROMCON_ROM_CONTROL, such as the VersaPortDisable bit of the register corresponding to ROMCON_ROM_CONTROL?

It is estimated that someone will do this:

The offset of the member RomControl (an example of ROMCON defined by ROMCON_ROM_CONTROL in ROMCON) with respect to ROMCON is predefined,

# define ROMCONRomControlOffset 0x8

Then design an interface to access the ROM, as shown below:

/* Read the register data of ROM controller at src position to dest*/

Typedef unsigned long dword _ t;;

void rom_read(dword_t* src,uint 32 _ t * dest);

void rom_write(dword_t* src,uint 32 _ t * dest);

Finally, use this offset to do the following:

ROMCON _ ROM _ CONTROL trom ctrl = { 0 };

dword _ t * pReg =(dword _ t *)(IO _ BASE+ROMCONOffset+ROMCONRomControlOffset);

rom_read(pReg,(uint 32 _ t)*(& amp; trom ctrl));

/* Check the VersaPortDisable bit in the register and enable it if it is not enabled */

If (! trom ctrl . nl union . nl struct . versaportdisable)

{

trom ctrl . nl union . nl struct . versaportdisable = 1;

rom_write(pReg,(uint 32 _ t)*(& amp; trom ctrl));

}

Only in this way can the purpose of accessing the corresponding register be truly achieved. However, if there are many registers related to ROM, isn't it inconvenient to define, remember and manage so many offsets? At this point, if you remember the previous offsetof, I think you might make the following optimization:

# define ROMCON _ ADDR(m)(((size _ t)IO _ BASE+\

(size_t)ROMCONOffset+\

(size_t)offsetof(ROMCON,m))

ROMCON _ ROM _ CONTROL trom ctrl = { 0 };

dword _ t * pReg =(dword _ t *)rom con _ ADDR(ConfigA);

rom_read(pReg,(uint 32 _ t)*(& amp; trom ctrl));

/* Check the VersaPortDisable bit of the register, and start it if it is not started */

If (! trom ctrl . nl union . nl struct . versaportdisable)

{

trom ctrl . nl union . nl struct . versaportdisable = 1;

rom_write(pReg,(uint 32 _ t)*(& amp; trom ctrl));

}