<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. 7 libjpeg使用

    一.交叉编译libjepg编译 tar xzf libjpeg-turbo-1.2.1.tar.gz ./configure –help ./configure --prefix=/work/proj ...

  2. C++C++ 指针(二)--c++ 指针(二)--c++

    一.内存管理:new和delete 1.new操作符:从操作系统获得内存块,并返回该内存块的首地址. delete操作符:将new申请的内存返还给操作系统. 开始一个简单的例子: #include & ...

  3. RPI学习--环境搭建_串口连接

    有两种, 一种是通过MAX2323芯片连接的串口,要接VCC为芯片供电. 另一种是通过PL2302芯片连接的USB,可不接VCC,用电脑USB口为芯片供电. 下面以通过MAX2323方式为例. 1,V ...

  4. Android 自定义属性

    values新建一个attrs.xml<resource>    <declare-styleable name = "MyTextView">    &l ...

  5. 迭代器(Iterator)模式

    转自:http://blog.csdn.net/lilu_leo/article/details/7609496 概述      迭代器(Iterator)模式,又叫做游标(Cursor)模式.GOF ...

  6. windows azure中国 里面建立一个虚拟机,与虚拟机建立通信 里面部署IIS,外网访问

    在windows azure中国 里面建立一个虚拟机,里面部署IIS,外网不能访问么? 外网访问的地址是给的那个DNS地址 ,比如我的是 DNS 名称 urbanairserver.cloudapp. ...

  7. 修改Centos SSH远程端口

    1. 在防火墙添加开放端口10000:本机防火墙和云防火墙 本机防火墙:-A INPUT -m state --state NEW -m tcp -p tcp --dport 10000 -j ACC ...

  8. poj2264 dp+路径

    //Accepted 208K 0MS //dp //最长公共子序列+路径 #include <cstdio> #include <cstring> #include < ...

  9. git vs svn

    http://www.tuicool.com/articles/e2MnAb Git与SVN的不同之处 svn为集中化的版本控制,svn获取最新的版本或者提交更新,历史记录等信息每次都要连接中央版本库 ...

  10. 将salt取到的数据处理

    #!/usr/bin/env python #coding:utf-8 import json with open('minfo') as f,open('minfoMiddle','w') as f ...