<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. obj.offsetHeight与obj.style.height区别

    我们都知道obj.offsetHeight与obj.style.height都可以获取obj的高度,但是在js使用中,我们通常会使用前者来获取高度,这是为什么,二者有什么样的区别呢. 1.obj.of ...

  2. PDF创建及动态转换控件程序包ActivePDF Portfolio

    ActivePDF Portfolio是将4个activePDF最优秀的服务器产品捆绑成一个价格适中的控件程序包.它提供了开发一个完整的服务器端的PDF解决方案所需的一切. 具体功能: activeP ...

  3. Xp 消息队列的使用

    1.安装消息队列3.0: 控制面板/添加删除程序/添加window组件/找到消息队列/选择->详细信息->MSMQ HTTP支持. 注意:如果计算机没有连接到域需要去掉Active Dir ...

  4. C# HttpBrowser 跨进程访问,解决内存泄露问题

    #undef DEBUG using Microsoft.Win32; using Newtonsoft.Json; using System; using System.Collections.Ge ...

  5. mybatis 的 resulttype 和resultMap

    resultType适合返回值比较简单的,比如一个数据类型,或者一个对象.比如对象的情况,是将表的列名和对象的属性一一对应的. 但是resultType无法处理返回值比较复杂的,特别是连接查询,需要用 ...

  6. 【iOS开发】企业版证书($299)In-House方式发布指南 (转)

    一.明确几个概念 1.企业版IDP:即iOS Development Enterprise Program.注意是$299/Year那种,并不是$99/Year的那种. 2.In House:是只企业 ...

  7. 《Head First设计模式(中文版)》

    <Head First设计模式(中文版)>共有14章,每章都介绍了几个设计模式,完整地涵盖了四人组版本全部23个设计模式.前言先介绍<Head First设计模式(中文版)>的 ...

  8. hdu3911 线段树 区间合并

    //Accepted 3911 750MS 9872K //线段树 区间合并 #include <cstdio> #include <cstring> #include < ...

  9. loaderexceptions

    前段时间遇到一个问题 从容器中取数据时老报一个“无法加载一个或多个请求,请检索loaderexceptions” 真心是不晓得什么问题 以前经常这么用没有问题的 这个是在网站下引用了别的已经编译好的别 ...

  10. 数组prototype添加函数呢,采用回调判定函数内容

    1.解决方案 Array.prototype.all = function (p) { return this.filter(p).length == this.length; }; Array.pr ...