使用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结构体指针
结构体指针 指针结构与指针的关系亦有两重:其一是在定义结构时,将指针作为结构中的一个成员:其二是指向结构的指针(称为结构指针). 前者同一般的结构成员一样可直接进行访问,后者是本节讨论的重点. 结构指 ...
随机推荐
- SSH学习
简介 SSH或Secure Shell是一种远程管理协议,允许用户通过Internet控制和修改远程服务器.该服务是作为未加密Telnet的安全替代品创建的,它使用加密技术确保与远程服务器之间的所有通 ...
- 第17章 社区快速入门和模板 - Identity Server 4 中文文档(v1.0.0)
IdentityServer组织不维护这些示例.IdentityServer组织愉快地链接到社区模板,但不能对模板做出任何保证.请直接与作者联系. 17.1 各种ASP.NET核心安全样本 https ...
- netfilter及iptables基本概念
网络访问控制 网络访问控制可以简单理解为防火墙,常用的网络访问控制有:哪些IP可以访问服务器, 可以使用哪些协议,哪些接口,是否需要对数据包进行修改等. netfilter netfilter是通过i ...
- JS实现图片base64转blob对象,压缩图片,预览图片,图片旋转到正确角度
base64转blob对象 /** 将base64转换为文件对象 * @param {String} base64 base64字符串 * */ var convertBase64ToBlob = f ...
- Dynamics 365测试和启用邮箱时候一直显示“安排电子邮件配置测试”怎么办?
摘要: 本人微信公众号:微软动态CRM专家罗勇 ,回复284或者20181125可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me ...
- MongoDB的存储结构及对空间使用率的影响
MongoDB的存储结构及对空间使用率的影响 使用MongoDB一段时间的同学肯定会发现,MongoDB往往会占用比实际数据大小多不少空间的问题.如果利用db.stats()命令去查看,会发现Mong ...
- pthread_exit在main线程中的用处
在main线程中调用pthread_exit会起到只让main线程退出,但是保留进程资源,供其他由main创建的线程使用,直至所有线程都结束,但在其他线程中不会有这种效果 https://stacko ...
- MySQL外键设置中的的 Cascade、NO ACTION、Restrict、SET NULL
例如: ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo FOREIGN KEY(gradeid) REFERENCES grade(id) ON DELET ...
- 导入JavaWeb 项目出现的问题
前言: 环境: windown 10 JDK 1.8 Tomcat 7 eclipse 导入项目 下面错误是出现的问题 Multiple annotations found at this line: ...
- adb server version doesn’t match this client
上传文件到海马玩模拟器 环境变量:ANDROID_SDK_HOME配置是D:\Android\android_sdk_windows 报错:adb server version (31) doesn' ...