使用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.
/*
This program was written by Richard Chad Sparrow
as a test case for AB-testing the hazard management
system.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
struct hazard {
char *description;
void *extraData;
int extraDataType;
int id;
char severityClass;
};
void printHazard(struct hazard *hazard);
int main(int argc, char **argv){
struct hazard hazard1;
struct hazard hazard2;
struct hazard *lastHazard;
/* Hazard data setup. */
hazard1.description = "Brake service required.";
hazard1.extraData = NULL;
hazard1.extraDataType = ;
hazard1.id = ;
hazard1.severityClass = 'A';
hazard2.description = "Unknown issue in fluid level.";
hazard2.extraData = NULL;
hazard2.extraDataType = ;
hazard2.id = ;
hazard2.severityClass = 'U';
lastHazard = &hazard2;
printf("Hazards after setup:\n");
printHazard(&hazard1);
printHazard(&hazard2);
/*
The brake service hazard has been present for multiple
services, so needs to be updated to severity class 'B'.
*/
/* Original: hazard1.severityClass = 'B'; */
/* CHANGE THE CODE HERE: */
hazard1.severityClass = 'B';
printf("Hazard 1 after class B severity update:\n");
printHazard(&hazard1);
/*
The next hazard to be evaluted has been evaluated and
its severity class has been found to be quite serious,
class 'D'. As part of this issue, the id has also been
increased to 3 and the hazard description has been
changed to "Fluid leak in tank 4".
*/
/* Original: lastHazard->severityClass = 'D'; */
/* CHANGE THE CODE HERE: */
lastHazard->severityClass = 'D'; /* Original: lastHazard->description = "Fluid leak in tank 4"; */
/* CHANGE THE CODE HERE: */
lastHazard->description = "Fluid leak in tank 4";
printf("Hazard 2 after description and D-class update:\n");
printHazard(&hazard2);
return ;
}
void printHazard(struct hazard *hazard){
printf("Hazard %d: %s [Class %c, extraDataType: %d]\n",
hazard->id, hazard->description, hazard->severityClass,
hazard->extraDataType);
}
即:不使用.和->替换目标代码,提示使用offsetof函数。
关于offsetof函数:http://man7.org/linux/man-pages/man3/offsetof.3.html
第一条:
hazard1.severityClass = 'B';
替换为:
//*(char *)((void *)(&hazard1) + offsetof(struct hazard, severityClass)) = 'B';
*(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根据成员在结构体中定义的顺序进行偏移。
最后一条:
lastHazard->description = "Fluid leak in tank 4";
替换为:
//*(char **)((void *)(lastHazard) + offsetof(struct hazard, description)) = "Fluid leak in tank 4";
//*(char **)((void *)(lastHazard)) = "Fluid leak in tank 4";
*(char **)(lastHazard) = "Fluid leak in tank 4";
lastHazard为结构体指针,故不用&,description为结构体中第一个成员,即结构体变量地址同时也是该成员的地址。
答案:
/*
This program was written by Richard Chad Sparrow
as a test case for AB-testing the hazard management
system.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
struct hazard {
char *description;
void *extraData;
int extraDataType;
int id;
char severityClass;
};
void printHazard(struct hazard *hazard);
int main(int argc, char **argv){
struct hazard hazard1;
struct hazard hazard2;
struct hazard *lastHazard;
/* Hazard data setup. */
hazard1.description = "Brake service required.";
hazard1.extraData = NULL;
hazard1.extraDataType = ;
hazard1.id = ;
hazard1.severityClass = 'A';
hazard2.description = "Unknown issue in fluid level.";
hazard2.extraData = NULL;
hazard2.extraDataType = ;
hazard2.id = ;
hazard2.severityClass = 'U';
lastHazard = &hazard2;
printf("Hazards after setup:\n");
printHazard(&hazard1);
printHazard(&hazard2);
/*
The brake service hazard has been present for multiple
services, so needs to be updated to severity class 'B'.
*/
/* Original: hazard1.severityClass = 'B'; */
/* CHANGE THE CODE HERE:
//hazard1.severityClass = 'B';*/ //*(char *)((void *)(&hazard1) + offsetof(struct hazard, severityClass)) = 'B';
*(char *)((void *)(&hazard1) + sizeof(char *) + sizeof(void *) + sizeof(int) + sizeof(int)) = 'B'; printf("Hazard 1 after class B severity update:\n");
printHazard(&hazard1);
/*
The next hazard to be evaluted has been evaluated and
its severity class has been found to be quite serious,
class 'D'. As part of this issue, the id has also been
increased to 3 and the hazard description has been
changed to "Fluid leak in tank 4".
*/
/* Original: lastHazard->severityClass = 'D'; */
/* CHANGE THE CODE HERE:
lastHazard->severityClass = 'D';*/ *(char *)((void *)(lastHazard) + offsetof(struct hazard, severityClass)) = 'D'; /* Original: lastHazard->description = "Fluid leak in tank 4"; */
/* CHANGE THE CODE HERE:
lastHazard->description = "Fluid leak in tank 4";*/ //*(char **)((void *)(lastHazard) + offsetof(struct hazard, description)) = "Fluid leak in tank 4";
//*(char **)((void *)(lastHazard)) = "Fluid leak in tank 4";
*(char **)(lastHazard) = "Fluid leak in tank 4"; printf("Hazard 2 after description and D-class update:\n");
printHazard(&hazard2);
return ;
}
void printHazard(struct hazard *hazard){
printf("Hazard %d: %s [Class %c, extraDataType: %d]\n",
hazard->id, hazard->description, hazard->severityClass,
hazard->extraDataType);
}
使用offsetof对结构体指针偏移操作的更多相关文章
- ctypes 操作 python 与 c++ dll 互传结构体指针
CMakeLists.txt # project(工程名) project(blog-3123958139-1) # add_library(链接库名称 SHARED 链接库代码) add_libra ...
- 嵌入式-C语言:通过结构体指针操作结构体内容
#include<stdio.h> #include<string.h> struct Student { char name[32]; int age; int height ...
- cdev成员结构体file_operations文件操作结构的分析
struct file_operations{ struct module *owner; // 指向拥有该结构的模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES loff_t ...
- 【C语言入门教程】7.3 结构体指针的定义和引用
C 语言中指针的操作非常灵活,它也能指向结构体变量对结构体变量进行操作.在学习结构指针之前,需要再次加深对指针的认识.声明指针变量时所使用的数据类型修饰符实际上的作用是定义指针访问内存的范围,如果指针 ...
- c语言结构体指针初始化
今天来讨论一下C中的内存管理. 记得上周在饭桌上和同事讨论C语言的崛起时,讲到了内存管理方面 我说所有指针使用前都必须初始化,结构体中的成员指针也是一样 有人反驳说,不是吧,以前做二叉树算法时,他的左 ...
- C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com
原文:C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | I ...
- c语言结构体指针必须初始化
先说结论 结构体指针需要初始化 结构体指针的成员指针同样需要初始化 结构体变量定义的时候就已经分配了内存空间,而上面两个确没有 struct test{ int i; struct buf *p;} ...
- C语言结构体指针初始化(转)
reference: https://www.cnblogs.com/losesea/archive/2012/11/15/2772526.html 今天来讨论一下C中的内存管理. 记得上周在饭桌上和 ...
- (C)struct结构体指针
结构体指针 指针结构与指针的关系亦有两重:其一是在定义结构时,将指针作为结构中的一个成员:其二是指向结构的指针(称为结构指针). 前者同一般的结构成员一样可直接进行访问,后者是本节讨论的重点. 结构指 ...
随机推荐
- 第14章 添加JavaScript客户端 - Identity Server 4 中文文档(v1.0.0)
本快速入门将展示如何构建基于浏览器的JavaScript客户端应用程序(有时称为" SPA "). 用户将登录IdentityServer,使用IdentityServer发出的访 ...
- 【转载】C#中自定义Sort的排序规则IComparable接口
C#中的List集合在排序的时候,如果不使用Lambda表达式进行排序的话,一般调用Sort()方法进行排序,如果希望Sort()方法排序后的结果跟我们预想的效果一致或者按照我们自定义的规则排序,则需 ...
- VBA批量导入图片到多Word文档并加标题(会飞的鱼)
感谢会飞的鱼大牛~ Public fp$, obmapp As Object Sub kk() 文件夹浏览器 Application.ScreenUpdating = False Set fso = ...
- C# 处理PPT水印(二)——去除水印效果(文本水印、图片水印)
本文将对C#处理PPT幻灯片中的水印进一步说明和介绍.在C# 处理PPT水印(一)一文中,分享了如何插入水印效果的方法,包括插入文字水印效果.插入图片作为水印效果两种情况,那对于不需要水印效果的情况, ...
- Java开发笔记(七十二)Java8新增的流式处理
通过前面几篇文章的学习,大家应能掌握几种容器类型的常见用法,对于简单的增删改和遍历操作,各容器实例都提供了相应的处理方法,对于实际开发中频繁使用的清单List,还能利用Arrays工具的asList方 ...
- 浅谈"n个球"和"m个盒子"之间的乱伦关系
无视标题,从我做起 update in 2018.10.1: 补充了"至多为1的四中情况" 这玩意儿的官方名字应该是叫"Twelvefold way",共用12 ...
- 免费开源ERP-成功案例分析(2)
Odoo用户案例 Odoo用户概要 关于Odoo全球的用户,我们来看一些数据: Odoo目前全球有300万使用者 Odoo系统上每天新创建的数据库超过1000个 Odoo和Word.Excel.Pow ...
- 在android studio中配置运行时签名
做项目的时候,有时需要用到第三方接口,而基本第三方接口都是要求我们要先进行签名.结果每次调试都得手动进行签名一次,实在麻烦.所以android studio提供了一种在运行的时候自动进行签名的方法,在 ...
- nginx配置静态项目
当nignx可以加载下面的这个service时 server { listen 8085; server_name 1.192.60.82; location / { root /etc/nginx ...
- Linux 环境 Java JDK 安装&基本配置
索引: 目录索引 参看代码 GitHub: jdk.txt 一.Linux (DeepinOS) 环境 1.官网下载 jdk-8u112-linux-x64.tar.gz 2.创建目录 mkdir - ...