<stddef.h>,顾名思义,就是标准定义。C语言里这个标准库里定义了一些类型,和宏定义。

<stddef.h>的内容:

类型:

ptrdiff_t : 是两个指针相减的结果的无符号整数类型。
size_t : 是sizeof操作符的结构的无符号类型。
wchar_t : 是一个整数类型,它范围内的值可以表示最大扩展字符集中所有成员的不同编码值。

宏:

NULL     : 展开为实现定义的空指针常量。
offsetof : offsetof(type, member-designator);展开为衣蛾size_t的整值常量表达式,它的值是从结构的起始位置(由type指定) 到结构成员(member-designator)的偏移量,以字节为单位,member-dedignator应该满足:
static type t; 然后表达式&(t.member-designator)就会计算一个地址常量(如果指定的成员是一个位域,则这种行为未定义)。

实现:

在看<stddef.h>的实现之前,需要知道一个内置头文件<yvals.h>,它里面定义了很多头文件中所需要的宏、其他量。

<yvals.h> :
typedef int _Ptrdifft;
typedef unsigned int _Size_t;
typedef unsigned short _Wchar_t;
#define _NULL (void*)0

这些定义在很多实现上都可以工作。然而,某些实现可能要求对他们中的一个或者多个进行修改,这就是对他们进行参数化的原因。

/* stddef.h standard header */
#ifndef _STDDEF
#define _STDDEF
#ifndef _YVALS
#include <yvals.h>
#endif /* macros */
#define NULL _NULL
#define offsetof(T, member) ((_Size_t)&((T*)0)->member) /* type definitions*/
#ifndef _SIZET
#define _SIZET
typedef _Size_t size_t;
#endif
#ifndef _WCHART
#define _WCHART
typedef _Wchar_t wchar_t;
#endif
typedef _Ptrdifft ptrdiff_t;
#endif

类型定义和NULL宏定义就没啥可说的,要么是预处理器干的事,要么就是类型别名,重点在offsetof实现。

#define offsetof(T, member) ((_Size_t)&((T*)0)->member)

T是一个结构体,member是T结构体中某一个成员,那么它是如何取到member在T中的偏移量呢?是这样的:

首先,将0这个int类型常量强制解释为一个地址值,这个地址的类型为T*,因此我们也就获取到T结构体的基址,然后再指向这个结构体成员的member成员,,我们就获取到了T结构体中member成员变量,然后&取到member的地址,当然,member的地址是相对于(T*)0的,就是相对于它所在结构体的基址的,因为这个基址的值为0,所以我们&运算符取到的member相对于(T*)0的地址就是T结构体中member的地址偏移量,我们可以想象到的,这个偏移量应该是个大于零的整数,所以我们就把这个偏移量显示类型转换为_Size_t,也就是unsigned
int了。

题外话:

与offsetof想对应的,在Linux内核代码中,也有个宏定义,它的作用是根据结构体中某个成员的地址来获取指向该结构体的指针:

#define container_of(ptr, type, member) ({                      \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

ptr是一个指向结构体成员的指针,typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型。type 是结构体名,member是该结构体的成员。

运作原理:

首先:typeof((type*)0->member) 将0解释为一个地址,类型为(type*),然后获得type结构体中的member成员,然后获取到成员变量的类型。它是基于0地址的。

然后:定义了一个临时的指向member的指针_mptr并且让它指向ptr所指向的地址。

接着:将_mptr转换为char类型指针,保证指针相减时是以一个字节为单位进行相减的。然后这里用了offsetof,它的值是member成员相对于type结构体的偏移量。然后指针就移动到了type的起始地址0,也就是这个结构体的类型指针了。然后将它显示转换为(type*),这样ptr中储存的就是type结构体类型的指针了。

《C标准库》——之<stddef.h>的更多相关文章

  1. 彻底弄清c标准库中string.h里的常用函数用法

    在我们平常写的c/c++程序,一些算法题中,我们常常会用到c标准库中string.h文件中的函数,这些函数主要用于处理内存,字符串相关操作,是很有用的工具函数.而且有些时候,在笔试或面试中也会出现让你 ...

  2. 走进C标准库(8)——"string.h"中函数的实现相关字符串操作函数

    我的strcat: char *strcat(char *dest,char *src) { char * reval = dest; while(*dest) dest++; while(*src) ...

  3. 走进C标准库(3)——"stdio.h"中的getc和ungetc

    接前文. 再来看看getc和ungetc的实现.在看这两个函数的实现之前,我们先来想一想这两个函数分别需要做的工作. int getc(FILE *stream) 说明:函数getc从stream指向 ...

  4. 走进C标准库(2)——"stdio.h"中的fopen函数

    其他的库文件看起来没有什么实现层面的知识可以探究的,所以,直接来看stdio.h. 1.茶余饭后的杂谈,有趣的历史 在过去的几十年中,独立于设备的输入输出模型得到了飞速的发展,标准C从这个改善的模型中 ...

  5. 走进C标准库(1)——assert.h,ctype.h

    默默觉得原来的阅读笔记的名字太土了,改了个名字,叫做走进C标准库. 自己就是菜鸟一只,第一次具体看C标准库,文章参杂了对<the standard C library>的阅读和对源码的一些 ...

  6. C 非标准库(conio.h)

    所谓的 C 标准库(C standard library),是指在 ISO C 或者 POSIX 标准中定义的: POSIX is a superset(超集) of the standard C l ...

  7. 走进C标准库(4)——"stdio.h"中的putc

    花了点时间把园子弄得好看了点,现在继续. 函数名: putc 功  能: 输出一字符到指定流中 用  法: int putc(int ch, FILE *stream); #define _putc_ ...

  8. 走进C标准库(5)——"stdio.h"中的其他部分函数

    函数介绍来自:http://ganquan.info/standard-c/ 函数名: freopen 功  能: 替换一个流 用  法: FILE *freopen(char *filename, ...

  9. 走进C标准库(6)——"string.h"中函数的实现memchr

    我写的memchr: void *memchr(const void *buf, char ch, unsigned count){ unsigned ; while(*(buf++) != ch & ...

  10. 走进C标准库(7)——"string.h"中函数的实现memcmp,memcpy,memmove,memset

    我的memcmp: int memcmp(void *buf1, void *buf2, unsigned int count){ int reval; while(count && ...

随机推荐

  1. NOIP2010解题报告

    今天状态不错..1个小时AC了前3题,第四题第一次也拿到了80%的分数,后来换了算法才拿到全部分数.. 第一题: 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章. 这个翻译软件的原 ...

  2. node开发 npm install -g express-generator@4

    Node forever : 1,forever start --uid test start app.js 2,forever start --uid test start -a app.js 3, ...

  3. Oracle连接的若干错误

    用PL/SQL连接Oracle时会抛若干错误,如下: 1.ora-12154:TNS:无法解析指定的连接标识符 答:plsql在%Oracle_Home%\Network\Admin或者c:\inst ...

  4. C#操作Access数据库(创建&修改结构)

    本文转自:http://www.cnblogs.com/liyugang/archive/2012/11/17/2775393.html 想要在程序中控制Access,不是数据,而是Access数据库 ...

  5. 企业需要k2来解放孤岛危机

    当我谈孤岛危机时,我谈些什么?你以为我要说的是一款风靡的游戏?那恐怕要让你失望了,今天要谈的是“企业管理体系孤岛”,但更多人甚至都没意识到这是危机. 下面的场景,也许你会觉得似曾相识. 场景一 某制鞋 ...

  6. 蓝桥杯 algo——6 安慰奶牛 (最小生成树)

    问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是一个奶牛的家.FJ计 划除去P条道路中尽可能多的道路 ...

  7. ios开发逆向传值的几种方法整理

    第一种:代理传值 第二个控制器: @protocol WJSecondViewControllerDelegate <NSObject> - (void)changeText:(NSStr ...

  8. julia与python中的列表解析.jl

    julia与python中的列表解析.jl #=julia与python中的列表解析.jl 2016年3月16日 07:30:47 codegay julia是一门很年轻的科学计算语言 julia文档 ...

  9. Quartz 2D--长方形和线条绘图

    今天原本想模仿2048 游戏的. 但是在设计背景环境时,涉及到绘图的知识!于是就开始对绘图进行了一翻学习. 若想自己绘图必须 写自己的View(继承UICView):然后重写UIView 中的 dra ...

  10. 安全增强 Linux (SELinux) 剖析

    架构和实现 Linux® 一直被认为是最安全的操作系统之一,但是通过引入安全增强 Linux(Security-Enhanced Linux,SELinux),National Security Ag ...