In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "what are the skills of using C pointer". The content of the explanation in this article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought. Let's study and learn "what are the skills for the use of C pointer"?
1. Appetizers: modify the data in the tone function
/ / Exchange 2 int data void demo1_swap_data (int * a, int * b) {int tmp = * a; * a = * b; * b = tmp;} void demo1 () {int I = 1; int j = 2; printf ("before: I =% d, j =% d\ n", I, j); demo1_swap_data (& I, & j) Printf ("after: I =% d, j =% d\ n", I, j);}
There is no need to explain this code, we can understand it at a glance. If you explain too much, it seems to insult your intelligence.
two。 In the called function, allocate system resources
The purpose of the code is to allocate size bytes of space from the heap in the called function and return it to the pData pointer in the main tone function.
Void demo2_malloc_heap_error (char * buf, int size) {buf = (char *) malloc (size); printf ("buf = 0x%x\ n", buf);} void demo2_malloc_heap_ok (char * * buf, int size) {* buf = (char *) malloc (size); printf ("* buf = 0x%x\ n", * buf);} void demo2 () {int size = 1024; char * pData = NULL / / incorrect usage demo2_malloc_heap_error (pData, size); printf ("& pData = 0x%x, pData = 0x%x\ n", & pData, pData); / / correct use of demo2_malloc_heap_ok (& pData, size); printf ("& pData = 0x%x, pData = 0x%x\ n", & pData, pData); free (pData);}
2.1 incorrect usage
When you first enter the called function demo2_malloc_heap_error, the parameter buff is a char* pointer, and its value is equal to the value of the pData variable, that is, the values of buff and pData are the same (both are NULL). The memory model is shown in the figure:
After executing the malloc statement in the called function, the address space applied from the heap is assigned to buf, that is, it points to the new address space, while NULL is still in pData. The memory model is as follows:
As you can see from the figure, pData has always had NULL in its memory and does not point to any heap space. In addition, because the formal parameter buf is placed in the stack area of the function, the requested space in the heap area is leaked when it is returned from the tuned function.
2.2 correct usage
When you first enter the called function demo2_malloc_heap_error, the parameter buf is a second-level pointer of char* type, that is, the value in buf is the address of another pointer variable. In this example, the value in buf is the address of the pointer variable pData. The memory model is as follows:
After executing the malloc statement in the called function, the address space requested from the heap area is assigned to * buf. Because buf = & pData, * buf is equivalent to pData. Then the address space applied from the heap area is assigned to the pData variable. The memory model is as follows:
After returning from the called function, pData correctly gets a piece of heap space, and don't forget to release it actively after using it.
3. Transfer function pointer
As we know from the previous article, the function name itself represents an address in which a series of scripts defined in the function body are stored. As long as the address is followed by a caller (parentheses), it is executed in this function. In the actual program, the function name is often passed as function parameters.
Friends who are familiar with C++ know that when performing various algorithm operations on container data in the standard library, you can pass in the algorithm functions provided by the user (if no functions are passed, the standard library will use the default).
The following is an example code that sorts an array of int rows. The last argument to the sorting function demo3_handle_data is a function pointer, so you need to pass in a specific sorting algorithm function. There are two candidate functions in the example that can be used:
Descending order: demo3_algorithm_decend
Ascending order: demo3_algorithm_ascend
Typedef int BOOL; # define FALSE 0 # define TRUE 1 BOOL demo3_algorithm_decend (int a, int b) {return a > b;} BOOL demo3_algorithm_ascend (int a, int b) {return a
< b; } typedef BOOL (*Func)(int, int); void demo3_handle_data(int *data, int size, Func pf) { for (int i = 0; i < size - 1; ++i) { for (int j = 0; j < size - 1 - i; ++j) { // 调用传入的排序函数 if (pf(data[j], data[j+1])) { int tmp = data[j]; data[j] = data[j + 1]; data[j + 1] = tmp; } } } } void demo3() { int a[5] = {5, 1, 9, 2, 6}; int size = sizeof(a)/sizeof(int); // 调用排序函数,需要传递排序算法函数 //demo3_handle_data(a, size, demo3_algorithm_decend); // 降序排列 demo3_handle_data(a, size, demo3_algorithm_ascend); // 升序排列 for (int i = 0; i < size; ++i) printf("%d ", a[i]); printf("\n"); } 这个就不用画图了,函数指针 pf 就指向了传入的那个函数地址,在排序的时候 直接调用就可以了。 4. 指向结构体的指针 在嵌入式开发中,指向结构体的指针使用特别广泛,这里以智能家居中的一条控制指令来举例。在一个智能家居系统中,存在各种各样的设备(插座、电灯、电动窗帘等),每个设备的控制指令都是不一样的,因此可以在每个设备的控制指令结构体中的最前面,放置所有指令都需要的、通用的成员变量,这些变量可以称为指令头(指令头中包含一个代表命令类型的枚举变量)。 当处理一条控制指令时,先用一个通用命令(指令头)的指针来接收指令,然后根据命令类型枚举变量来区分,把控制指令强制转换成具体的那个设备的数据结构,这样就可以获取到控制指令中特定的控制数据了。 本质上,与 Java/C++ 中的接口、基类的概念类似。 // 指令类型枚举 typedef enum _CMD_TYPE_ { CMD_TYPE_CONTROL_SWITCH = 1, CMD_TYPE_CONTROL_LAMP, } CMD_TYPE; // 通用的指令数据结构(指令头) typedef struct _CmdBase_ { CMD_TYPE cmdType; // 指令类型 int deviceId; // 设备 Id } CmdBase; typedef struct _CmdControlSwitch_ { // 前 2 个参数是指令头 CMD_TYPE cmdType; int deviceId; // 下面都有这个指令私有的数据 int slot; // 排插上的哪个插口 int state; // 0:断开, 1:接通 } CmdControlSwitch; typedef struct _CmdControlLamp_ { // 前 2 个参数是指令头 CMD_TYPE cmdType; int deviceId; // 下面都有这个指令私有的数据 int color; // 颜色 int brightness; // 亮度 } CmdControlLamp; // 参数是指令头指针 void demo4_control_device(CmdBase *pcmd) { // 根据指令头中的命令类型,把指令强制转换成具体设备的指令 if (CMD_TYPE_CONTROL_SWITCH == pcmd->CmdType) {/ / Type cast CmdControlSwitch * cmd = pcmd; printf ("control switch. Slot =% d, state =% d\ n ", cmd- > slot, cmd- > state);} else if (CMD_TYPE_CONTROL_LAMP = = pcmd- > cmdType) {/ / Type cast CmdControlLamp * cmd = pcmd; printf (" control lamp. Color = 0x%x, brightness =% d\ n ", cmd- > color, cmd- > brightness);}} void demo4 () {/ / instruction 1: control a switch CmdControlSwitch cmd1 = {CMD_TYPE_CONTROL_SWITCH, 1,3,0}; demo4_control_device (& cmd1); / / instruction 2: control a light bulb CmdControlLamp cmd2 = {CMD_TYPE_CONTROL_LAMP, 2, 0x112233, 90} Demo4_control_device (& cmd2);}
5. Function pointer array
This example was demonstrated in the previous article, and for completeness, it will be posted here again.
Int add (int a, int b) {return a + b;} int sub (int a, int b) {return a-b;} int mul (int a, int b) {return a * b;} int divide (int a, int b) {return a / b;} void demo5 () {int a = 4, b = 2; int (* p [4]) (int, int); p [0] = add; p [1] = sub P [2] = mul; p [3] = divide; printf ("% d +% d =% d\ n", a, b, p [0] (a, b)); printf ("% d -% d =% d\ n", a, b, p [1] (a, b)); printf ("% d *% d =% d\ n", a, b, p [2] (a, b) Printf ("% d /% d =% d\ n", a, b, p [3] (a, b));}
6. Using flexible arrays in structures
Without explaining the concept, let's look at a code example:
/ / A structure where the member variable data is the pointer typedef struct _ ArraryMemberStruct_NotGood_ {int num; char * data;} ArraryMemberStruct_NotGood; void demo6_not_good () {/ / print the memory size of the structure int size = sizeof (ArraryMemberStruct_NotGood); printf ("size =% d\ n", size); / / assign a structure pointer ArraryMemberStruct_NotGood * ams = (ArraryMemberStruct_NotGood *) malloc (size) Ams- > num = 1; / allocate space for data pointers in structures ams- > data = (char *) malloc (1024); strcpy (ams- > data, "hello"); printf ("ams- > data =% s\ n", ams- > data); / / print address printf of structure pointers and member variables ("ams = 0x%x\ n", ams) Printf ("ams- > num = 0x%x\ n", & ams- > num); printf ("ams- > data = 0x%x\ n", ams- > data); / / Free free (ams- > data); free (ams);}
On my computer, the printed results are as follows:
You can see that the structure has a total of 8 bytes (4 bytes for int and 4 bytes for pointer).
The data member in the structure is a pointer variable, and you need to apply for a separate piece of space for it to use. And after the structure is used, you need to release data first, and then release the structure pointer ams, in the right order. Isn't it a bit troublesome to use it this way?
Therefore, the C99 standard defines a syntax: flexible array member (flexible array), which goes directly to the code (if the following code encounters a warning during compilation, please check the compiler's support for this syntax):
/ / A structure whose member variable is an array typedef struct _ ArraryMemberStruct_Good_ {int num; char data [];} ArraryMemberStruct_Good; void demo6_good () {/ / print the size of the structure int size = sizeof (ArraryMemberStruct_Good); printf ("size =% d\ n", size) / / allocate space for structure pointers ArraryMemberStruct_Good * ams = (ArraryMemberStruct_Good *) malloc (size + 1024); strcpy (ams- > data, "hello"); printf ("ams- > data =% s\ n", ams- > data); / / print address printf of structure pointers and member variables ("ams = 0x%x\ n", ams) Printf ("ams- > num = 0x%x\ n", & ams- > num); printf ("ams- > data = 0x%x\ n", ams- > data); / / Free free (ams);}
The print result is as follows:
There are several differences from the first example:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
The size of the structure becomes 4.
When allocating space for the structure pointer, in addition to the size of the structure itself, the amount of space required by the data is also applied.
There is no need to allocate space separately for data.
When you free up space, you can release the structure pointer directly.
Is it much easier to use?! This is the advantage of flexible arrays.
Syntactically, a flexible array refers to an array in which the number of the last elements in the structure is unknown, or it can be understood that the length is 0, so the structure can be called variable length.
As mentioned earlier, the array name represents an address, which is an immutable address constant. In the structure, the array name is just a symbol, represents only an offset, and does not take up specific space.
In addition, flexible arrays can be of any type. You can see a lot of examples here, and this kind of usage is often seen in many communication processing scenarios.
7. Get the offset of the member variable in the structure through the pointer
The title seems to be a bit of a mouthful to read, split: in a structure variable, you can use the pointer manipulation technique to obtain the address of a member variable, the start address of the structure variable, and the offset between the structure variables.
You can see that this technique is used in many places in the Linux kernel code, as follows:
# define offsetof (TYPE, MEMBER) ((size_t) & (TYPE*) 0)-> MEMBER)) typedef struct _ OffsetStruct_ {int a; int b; int c;} OffsetStruct; void demo7 () {OffsetStruct os; / / print the address of the structure variable and member variable printf ("& os = 0x%x\ n", & os) Printf (& os- > a = 0x%x\ n ", & os.a); printf (" & os- > b = 0x%x\ n ", & os.b); printf (" & os- > c = 0x%x\ n ", & os.c); printf (" =\ n ") / / print the offset printf ("offset: a =% d\ n", (char *) & os.a-(char *) & os) between the member variable address and the structure variable start address; printf ("offset: B =% d\ n", (char *) & os.b-(char *) & os) Printf ("offset: C =% d\ n", (char *) & os.c-(char *) & os); printf ("=\ n"); / / obtain the offset printf ("offset: a =% d\ n", (size_t) & (OffsetStruct*) 0)-> a) Printf ("offset: B =% d\ n", (size_t) & ((OffsetStruct*) 0)-> b); printf ("offset: C =% d\ n", (size_t) & (OffsetStruct*) 0)-> c); printf ("=\ n"); / / use macro definitions to obtain the offset printf of member variables ("offset: a =% d\ n", offsetof (OffsetStruct, a)) Printf ("offset: B =% d\ n", offsetof (OffsetStruct, b)); printf ("offset: C =% d\ n", offsetof (OffsetStruct, c));}
First, let's take a look at the printed results:
There is no need to explain the print information of the first four lines, just look at the memory model below.
The following statement does not need much explanation, but subtracts the values of the two addresses to get the offset from the starting address of the structure variable. Note: the address needs to be strongly converted to char* before it can be subtracted.
Printf ("offset: a =% d\ n", (char *) & os.a-(char *) & os)
The following sentence needs to be well understood:
Printf ("offset: a =% d\ n", (size_t) & (OffsetStruct*) 0)-> a)
The number 0 is thought of as an address, that is, a pointer. As explained in the previous article, the pointer represents a piece of space in memory, and as for what you think of the data in this space, you can tell the compiler, and the compiler will manipulate the data as you want.
Now we think of the data in the address 0 as an OffsetStruct structure variable (told to the compiler through a cast), so we get an OffsetStruct structure pointer (green line in the image below), then get the member variable a (blue line) in the pointer variable, and then get the address of a (orange line) by taking the address symbol Finally, the address is forcibly converted to the size_t type (red line).
Because the structure pointer variable starts at the 0 address, the address of the member variable an is the offset of a from the start address of the structure variable.
If the process described above feels like a mouthful, please read it several times in conjunction with the following picture:
If the above figure can be understood, then the last print statement to obtain the offset through the macro definition will also be understood. It is nothing more than abstracting the code into a macro definition, which is easy to call:
# define offsetof (TYPE, MEMBER) ((size_t) & ((TYPE*) 0)-> MEMBER) printf ("offset: a =% d\ n", offsetof (OffsetStruct, a))
Some friends may ask: what's the use of getting this offset? Then please move on to example 8 below.
8. Get the pointer of the structure through the pointer of the member variable in the structure
The title is also a mouthful, directly combined with the code:
Typedef struct _ OffsetStruct_ {int a; int b; int c;} OffsetStruct
Suppose there is an OffsetStruct structure variable os, and we only know the address (pointer) of the member variable c in os, so what should we do if we want to get the address (pointer) of the variable os? This is the purpose described in the title.
The macro definition container_of in the following code is also from the Linux kernel (people usually dig more when they are free, and you can find a lot of good things).
# define container_of (ptr, type,member) ({\ const typeof (type *) 0)-> member) * _ mptr = (ptr);\ (type *) ((char *) _ mptr-offsetof (type,member));}) void demo8 () {/ / the following three lines just demonstrate the use of the typeof keyword int n = 1; typeof (n) m = 2 / / define the same type of variable m printf ("n =% d, m =% d\ n", n, m); / define the structure variable and initialize OffsetStruct os = {1,2,3}; / / print the address of the structure variable and the value of the member variable (for later verification) printf ("& os = 0x%x\ n", & os) Printf ("os.a =% d, os.b =% d, os.c =% d\ n", os.a, os.b, os.c); printf ("=\ n"); / / assume only the address of a member variable int * pc = & os.c; OffsetStruct * p = NULL / / according to the address of the member variable, get the address of the structure variable p = container_of (pc, OffsetStruct, c); / / print the address of the pointer, the value of the member variable printf ("p = 0x%x\ n", p); printf ("p-> a =% d, p-> b =% d, p-> c =% d\ n", p-> a, p-> b, p-> c);}
First, take a look at the printed results:
First of all, be clear about the types of parameters in the macro definition:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Ptr: pointer to member variables
Type: structure type
Member: the name of the member variable
The key point here is to understand the macro definition container_of. Combined with the following figure, the macro definition is taken apart to describe it:
Clause 1 statement analysis in macro definition:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Green line: think of the number 0 as a pointer and strongly convert it to the structure type type
Blue line: gets the member variable member in the pointer to the structure
Orange line: use the typeof keyword to get the type of the member, and then define a pointer variable of this type _ _ mptr
Red line: assign the macro parameter ptr to the _ _ mptr variable
Sentence Analysis of Article 2 in Macro definition:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
Green line: using the offset macro definition in demo7, we can get the offset of the member variable member from the start address of the structure variable, and this member variable pointer has just been known, which is _ _ mptr
Blue line: take the address _ _ mptr, minus its own offset from the start address of the structure variable, and you get the start address of the structure variable
Orange horizontal line: finally, convert this pointer (which is now char*) to a pointer of structure type type.
Thank you for your reading. These are the contents of "what are the skills for the use of C pointers?" after the study of this article, I believe you have a deeper understanding of the skills of using C pointers. The specific use of the situation also needs to be verified by practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.