<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. php 获取当前url,可以规避框架url重写后还有index.php的情况

    function get_url(){ $pageURL = 'http'; if ($_SERVER["HTTPS"] == "on") { $pageURL ...

  2. 在oracle中使用Trigger

    1.初始目标 在对表h1插入一条数据时,同时插入一条重复的数据(只有主键不同) 2.在PL/SQL里New一个Trigger或者手动敲入代码 先说明一下,表h1包括4列ID.C1.C2.C3 crea ...

  3. Codeforces Round #326 (Div. 2)-Duff and Meat

    题意: Duff每天要吃ai千克肉,这天肉的价格为pi(这天可以买好多好多肉),现在给你一个数值n为Duff吃肉的天数,求出用最少的钱满足Duff的条件. 思路: 只要判断相邻两天中,今天的总花费 = ...

  4. ctime、atime

    Linux系统文件有三个主要的时间属性,分别是ctime(change time, 而不是create time), atime(access time), mtime(modify time).后来 ...

  5. ACM - KMP题目小结 (更新中)

    KMP算法题型大致有两类,一类是next数组的应用,一类是匹配问题. next数组大多数是求字符串周期,或者是与前缀后缀有关,也可以应用在DP中.需要对next数组有一定理解才能做得出. next数组 ...

  6. c#多线程(UI线程,控件显示更新) Invoke和BeginInvoke 区别

    如果只是直接使用子线程访问UI控件,直接看内容三,如果想深入了解从内容一看起. 一.Control.Invoke和BeginInvoke方法的区别 先上总结: Control.Invoke 方法 (D ...

  7. SVG 2D入门7 - 重用与引用

    前面介绍了很多的图形元素,如果很多图形本身是一样的,需要每次都去定义一个新的么?我们能否共用一些图形呢?这是这节的重点 - SVG元素的重用. 组合 - g元素      g元素是一种容器,它组合一组 ...

  8. sql 字段重复值,in,like

  9. Linux下控制器IO地址

    在Linux下使用cat /proc/ioports可以查看控制器使用的IO地址范围

  10. Koch曲线

    Koch曲线是一种分形,完整的Koch曲线像雪花,维基百科上记录Koch曲线最早出现在海里格·冯·科赫的论文<关于一条连续而无切线,可由初等几何构作的曲线>中,它的定义如下,给定线段AB, ...