<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. comboBox的id返回System.Data.DataRowView

    关系到ComboBox的DataSource,DisplayMember和ValueMember属性的设置顺序的问题. ComboBox的DataSource属性为object类型,但是需要实现ILi ...

  2. UITableViewStyleGrouped 模式下 headview 多出一块高度问题

    self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.tableView ...

  3. 【STL】-priority_queue的用法

    初始化: priority_queue<int>                           maxPQ; priority_queue<int,vector<int& ...

  4. 【C语言学习】-04 一维数组、字符数组

    一维数组.数组排序.字符数

  5. HTTPS Everywhere – 保障隐私和信息安全的利器

    HTTPS Everywhere 是一款 Chrome 扩展程序,对于支持 HTTPS 的网站默认打开 HTTPS 加密传输来保障信息安全(HTTPS 介绍). HTTPS Everywhere 受到 ...

  6. 添加Sql作业,新建步骤出现:从IClassFactory为CLSID为{AA40D1D6-CAEF-4A56-B9BB-D0D3DC976BA2}的COM组件创建实例失败,原因是出现以下错误:c001f011。的解决方法

    32位操作系统: 打开运行(命令提示符), 一.输入 cd c:\windows\system32 进入到c:\windows\system32路径中 二.输入 regsvr32 "C:\P ...

  7. 获取一个 app 的 URL Scheme 的方法:

    获取一个 app 的 URL Scheme 的方法: 上这个网站 URL Schemes 查一下相应的 app 的 URL Scheme 是否有被收录 第一种方法没找到的话,把相应的 app 的 ip ...

  8. phpunit+selenium环境搭建

    这个环境搭建遇到了挺多麻烦,最终还是没能自己解决,幸好有同事“青蛙”的帮忙解决了这个问题!在这里把本人亲测步骤给大家列一下,希望给大家提供方便! 安装pear: Go-pear.phar下载地址:ht ...

  9. 原生javascript Ajax

    代码 1. IE5 ,IE6 使用ActiveXObject对象,   其余现代浏览器都支持XMLHttpRequest对象: function ajaxObject(){ var xmlhttp; ...

  10. Kylin上chromium不能用flash的解决命令

    sudo apt-get update sudo apt-get install pepperflashplugin-nonfree sudo update-pepperflashplugin-non ...