1. 程式人生 > >使用offsetof對結構體指針偏移操作

使用offsetof對結構體指針偏移操作

cast note argc b- .com main 替換 creation tof

題目來自於COMP20003 Tutorial 2:

Program m ing Challenge 2.2 The technology stack at Hidebound Inc. uses a subset of C w hich doesn‘t have the ‘.‘ or ‘->‘
operators, as the higher-ups heard shortcuts like this w ere useful in an activity called "code golfing" and, misunderstanding w hat
that meant, w anted to discourage all recreational activities on company time. The change improved compile times and required
resources slightly so the developer in charge of that performance w as happy to force the change on the other programmers in
the company. In this challenge, you‘ll need to replace a piece of code w hich does this using both the simple ‘->‘ and ‘.‘ operators
w ith a piece of code that instead changes the value in the struct by using value casting and pointer addition instead.
This challenge is intended to highlight that ‘.‘ and ‘->‘ are merely shortcuts to other dereference operations and though you w ill
eventually find your code is less messy w hen using them, understanding exactly w hat you are doing w ill reduce the number of
errors you make and allow you to examine code closely w hen you have something complicated that isn‘t doing exactly w hat you
think it should be. You may find reading through the (2nd) extra w orkshop material document on the LMS under the Resources
section is particularly useful for this task.
As a hint, you may find the offsetof macro useful (you can find this using the man pages). For an extra challenge, try only using
the sizeof macro, the address of operator (&) and the dereference operator (*). Note also that for the latter, a process know n as
"packing" may sometimes add holes to structs w hich are unused, though that has been carefully avoided in the struct defined
here.

 1 /*
 2 This program was written by Richard Chad Sparrow
 3 as a test case for AB-testing the hazard management
 4 system.
 5 */
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <stddef.h>
 9 struct hazard {
10     char *description;
11     void *extraData;
12     int extraDataType;
13 int id; 14 char severityClass; 15 }; 16 void printHazard(struct hazard *hazard); 17 int main(int argc, char **argv){ 18 struct hazard hazard1; 19 struct hazard hazard2; 20 struct hazard *lastHazard; 21 /* Hazard data setup. */ 22 hazard1.description = "Brake service required.
"; 23 hazard1.extraData = NULL; 24 hazard1.extraDataType = 0; 25 hazard1.id = 1; 26 hazard1.severityClass = A; 27 hazard2.description = "Unknown issue in fluid level."; 28 hazard2.extraData = NULL; 29 hazard2.extraDataType = 0; 30 hazard2.id = 2; 31 hazard2.severityClass = U; 32 lastHazard = &hazard2; 33 printf("Hazards after setup:\n"); 34 printHazard(&hazard1); 35 printHazard(&hazard2); 36 /* 37 The brake service hazard has been present for multiple 38 services, so needs to be updated to severity class ‘B‘. 39 */ 40 /* Original: hazard1.severityClass = ‘B‘; */ 41 /* CHANGE THE CODE HERE: */ 42 hazard1.severityClass = B; 43 printf("Hazard 1 after class B severity update:\n"); 44 printHazard(&hazard1); 45 /* 46 The next hazard to be evaluted has been evaluated and 47 its severity class has been found to be quite serious, 48 class ‘D‘. As part of this issue, the id has also been 49 increased to 3 and the hazard description has been 50 changed to "Fluid leak in tank 4". 51 */ 52 /* Original: lastHazard->severityClass = ‘D‘; */ 53 /* CHANGE THE CODE HERE: */ 54 lastHazard->severityClass = D; 55 56 /* Original: lastHazard->description = "Fluid leak in tank 4"; */ 57 /* CHANGE THE CODE HERE: */ 58 lastHazard->description = "Fluid leak in tank 4"; 59 printf("Hazard 2 after description and D-class update:\n"); 60 printHazard(&hazard2); 61 return 0; 62 } 63 void printHazard(struct hazard *hazard){ 64 printf("Hazard %d: %s [Class %c, extraDataType: %d]\n", 65 hazard->id, hazard->description, hazard->severityClass, 66 hazard->extraDataType); 67 }

即:不使用.和->替換目標代碼,提示使用offsetof函數。

關於offsetof函數:http://man7.org/linux/man-pages/man3/offsetof.3.html

第一條:

1 hazard1.severityClass = B;

替換為:

1     //*(char *)((void *)(&hazard1) + offsetof(struct hazard, severityClass)) = ‘B‘;
2     *(char *)((void *)(&hazard1) + sizeof(char *) + sizeof(void *) + sizeof(int) + sizeof(int)) = B;

為何是(void *)(&hazard1)?

&hazard1代表了該結構體變量和其首成員的地址,直接+1或者(struct hazard *)(&hazard1)+1則直接跳出了該結構體變量的範圍(如數組int a[10]:*(a+1)是a[1]一樣),使用(void *)讓其以字節為單位進行偏移(也可用(char *)),這樣就不會跳出該結構體變量了。 源自Psrion對我提出問題的回答https://q.cnblogs.com/q/111494/

也可使用sizeof根據成員在結構體中定義的順序進行偏移。

最後一條:

1 lastHazard->description = "Fluid leak in tank 4";

替換為:

1     //*(char **)((void *)(lastHazard) + offsetof(struct hazard, description)) = "Fluid leak in tank 4";
2     //*(char **)((void *)(lastHazard)) = "Fluid leak in tank 4";
3     *(char **)(lastHazard) = "Fluid leak in tank 4";

lastHazard為結構體指針,故不用&,description為結構體中第一個成員,即結構體變量地址同時也是該成員的地址。

答案:

 1 /*
 2 This program was written by Richard Chad Sparrow
 3 as a test case for AB-testing the hazard management
 4 system.
 5 */
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <stddef.h>
 9 struct hazard {
10     char *description;
11     void *extraData;
12     int extraDataType;
13     int id;
14     char severityClass;
15 };
16 void printHazard(struct hazard *hazard);
17 int main(int argc, char **argv){
18     struct hazard hazard1;
19     struct hazard hazard2;
20     struct hazard *lastHazard;
21     /* Hazard data setup. */
22     hazard1.description = "Brake service required.";
23     hazard1.extraData = NULL;
24     hazard1.extraDataType = 0;
25     hazard1.id = 1;
26     hazard1.severityClass = A;
27     hazard2.description = "Unknown issue in fluid level.";
28     hazard2.extraData = NULL;
29     hazard2.extraDataType = 0;
30     hazard2.id = 2;
31     hazard2.severityClass = U;
32     lastHazard = &hazard2;
33     printf("Hazards after setup:\n");
34     printHazard(&hazard1);
35     printHazard(&hazard2);
36     /*
37     The brake service hazard has been present for multiple
38     services, so needs to be updated to severity class ‘B‘.
39     */
40     /* Original: hazard1.severityClass = ‘B‘; */
41     /* CHANGE THE CODE HERE: 
42     //hazard1.severityClass = ‘B‘;*/
43     
44     //*(char *)((void *)(&hazard1) + offsetof(struct hazard, severityClass)) = ‘B‘;
45     *(char *)((void *)(&hazard1) + sizeof(char *) + sizeof(void *) + sizeof(int) + sizeof(int)) = B;
46     
47     printf("Hazard 1 after class B severity update:\n");
48     printHazard(&hazard1);
49     /*
50     The next hazard to be evaluted has been evaluated and
51     its severity class has been found to be quite serious,
52     class ‘D‘. As part of this issue, the id has also been
53     increased to 3 and the hazard description has been
54     changed to "Fluid leak in tank 4".
55     */
56     /* Original: lastHazard->severityClass = ‘D‘; */
57     /* CHANGE THE CODE HERE: 
58     lastHazard->severityClass = ‘D‘;*/
59     
60     *(char *)((void *)(lastHazard) + offsetof(struct hazard, severityClass)) = D;
61     
62     /* Original: lastHazard->description = "Fluid leak in tank 4"; */
63     /* CHANGE THE CODE HERE: 
64     lastHazard->description = "Fluid leak in tank 4";*/
65     
66     //*(char **)((void *)(lastHazard) + offsetof(struct hazard, description)) = "Fluid leak in tank 4";
67     //*(char **)((void *)(lastHazard)) = "Fluid leak in tank 4";
68     *(char **)(lastHazard) = "Fluid leak in tank 4";
69     
70     printf("Hazard 2 after description and D-class update:\n");
71     printHazard(&hazard2);
72     return 0;
73 }
74 void printHazard(struct hazard *hazard){
75     printf("Hazard %d: %s [Class %c, extraDataType: %d]\n",
76     hazard->id, hazard->description, hazard->severityClass,
77     hazard->extraDataType);
78 }

使用offsetof對結構體指針偏移操作