使用offsetof对结构体指针偏移操作

题目来自于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 }

 

转载于:https://www.cnblogs.com/Will-zyq/p/10049052.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:C语言字节对齐问题详解中提到了C语言中的字节对齐问题。在结构体中,为了提高内存访问的效率,编译器会对结构体进行字节对齐。这意味着结构体的成员在内存中并不是紧凑排列的,而是按照一定的规则进行对齐。具体的对齐规则取决于编译器和编译选项。\[1\] 引用\[2\]:在C语言中,可以使用offsetof来获取结构体成员相对于结构体开头的字节偏移量。这个宏非常有用,可以帮助我们计算出每个结构体成员相对于结构体开头的偏移字节数。通过这个宏,我们可以更好地理解结构体的内存布局。\[2\] 引用\[3\]:在C语言中,指针结构体的组合常常用于处理复杂的数据结构。指针可以指向结构体的成员,通过指针可以方便地对结构体进行操作指针结构体的组合可以实现更灵活的数据处理和内存管理。\[3\] 综上所述,C语言中的指针结构体组合可以用于处理复杂的数据结构,而字节对齐问题则是在结构体中为了提高内存访问效率而进行的优化。通过使用offsetof,我们可以更好地理解结构体的内存布局。 #### 引用[.reference_title] - *1* *3* [结构体指针,C语言结构体指针详解](https://blog.csdn.net/weixin_34069265/article/details/117110735)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [C语言之结构体详解](https://blog.csdn.net/m0_70749276/article/details/127061692)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值