在C语言中,我们可以自定义各种各样的数据结构,用来把很多数据保存在一个变量里面,但是每种数据结构都有自己的优缺点,PHP内核规模如此庞大,是否已经找到了一些非常棒的解决方法呢?
我们在选择各种数据结构时,往往会考虑我们需要处理的数据规模以及需要的性能。下面让我们简要的看一下看C语言中数组和链表的一些事情。
数组
作者这里用的不是Array,而是Vector,可能指的是C++里的Vector,它与数组几乎是完全一样的,唯一的不同便是可以实现动态存储。本节下文都是用数组一词代替之,请各位注意。数组是内存中一块连续的区域,其每一个元素都具有一个唯一的下标值。
不仅是整数,其它类型的变量也可以保存在数组中,比如我们前面用到的zend_get_parameters_array_ex(),便把很多zval**类型的变量保存到一个数组里,为了使其正常工作,我们提前向系统申请了相应大小的内存空间。
1 |
zval ***args = safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval**), 0); |
这里我们仍然可以用一个整数来当作下标去数组中取出我们想要的数据,就像var_dump()的实现中通过args[i]来获取参数并把它传递给php_var_dump()函数那样。
使用数组最大的好处便是速度!读写都可以在O(1)内完成,因为它每个元素的大小都是一致的,只要知道下标,便可以瞬间计算出其对应的元素在内存中的位置,从而直接取出或者写入。
链表
链表也是一种经常被使用的一种数据结构。链表中的每一个元素都至少有两个元素,一个指向它的下一个元素,一个用来存放它自己的数据,就像下面定义的那样:
1 |
typedef struct _namelist namelist; |
4 |
struct _namelist *next; |
我们可以声明一个其类型的元素:
1 |
static namelist *people; |
假设每一个元素都代表一个人,元素中的name属性便是这个人的名字,我们通过这样的语句来得到它:people->name; 第二个属性指向后面的一个元素,那我们便可以这样来访问下一个人的名字:people->next->name, 或者下一个人的下一个人的名字:people->next->next->name,一次类推,直到next的值是NULL,代表结 束。
2 |
void name_show(namelist *p) |
6 |
printf("Name: %s\n", p->name); |
链表可以被用来实现FIFO模式,达到先进者先出的目的!
01 |
static namelist *people = NULL, *last_person = NULL; |
02 |
void name_add(namelist *person) |
06 |
/* No one in the list yet */ |
07 |
people = last_person = person; |
10 |
/* Append new person to the end of the list */ |
11 |
last_person->next = person; |
13 |
/* Update the list tail */ |
16 |
namelist *name_pop(void) |
18 |
namelist *first_person = people; |
20 |
people = people->next; |
这样,我们便可以随意的向这个链表中添加或者删除数据,而不向数组那样,谨慎的考虑是否越界等问题。
上面实现的结构的学名叫做单向链表,也有地方叫单链表,反正是比较简单的意思~。它有一个致命的缺点,就是我们在插入或者读取某条数据的时候,都需 要从这个链表的开始,一个个元素的向下寻找,直到找到这个元素为止。如果链表中的元素比较多,那它很容易成为我们程序中的CPU消耗大户,进而引起性能问 题。为了解决这个问题,先人们发明了双向链表:
1 |
typedef struct _namelist namelist; |
改动其实不大,就是在每个元素中都添加了一个prev属性,用来指向它的上一个元素。
01 |
void name_add(namelist *person) |
06 |
/* No one in the list yet */ |
07 |
people = last_person = person; |
11 |
/* Append new person to the end of the list */ |
12 |
last_person ->next = person; |
13 |
person->prev = last_person; |
15 |
/* Update the list tail */ |
单单通过上面的程序你还体会不到它的好处,但是设想一下,如果现在你有这个链表中其中一个元素的地址,并且想把它从链表中删除,那我们该怎么做呢?如果是单向链表的话,我们只能这样做:
01 |
void name_remove(namelist *person) |
04 |
if (person == people) { |
05 |
/* Happens to be the first person in the list */ |
06 |
people = person->next; |
07 |
if (last_person == person) { |
08 |
/* Also happens to be the last person */ |
13 |
/* Search for prior person */ |
16 |
if (p->next == person) { |
18 |
p->next = person->next; |
19 |
if (last_person == person) { |
20 |
/* This was the last element */ |
27 |
/* Not found in list */ |
现在让我们来看看双向链表是怎样来处理这个问题的:
01 |
void name_remove(namelist *person) |
03 |
if (people == person) { |
04 |
people = person->next; |
06 |
if (last_person == person) { |
07 |
last_person = person->prev; |
11 |
person->prev->next = person->next; |
14 |
person->next->prev = person->prev; |
对元素的遍历查找不见了,取而代之的是一个O(1)的运算,这将极大的提升我们程序的性能。
- PHP内核探索之变量(4)- 数组操作
上一节(PHP内核探索之变量(3)- hash table),我们已经知道,数组在PHP的底层实际上是HashTable(链接法解决冲突),本文将对最常用的函数系列-数组操作的相关函数做进一步的跟踪. ...
- php内核探索 [转]
PHP内核探索:从SAPI接口开始 PHP内核探索:一次请求的开始与结束 PHP内核探索:一次请求生命周期 PHP内核探索:单进程SAPI生命周期 PHP内核探索:多进程/线程的SAPI生命周期 PH ...
- PHP内核探索之变量(3)- hash table
在PHP中,除了zval, 另一个比较重要的数据结构非hash table莫属,例如我们最常见的数组,在底层便是hash table.除了数组,在线程安全(TSRM).GC.资源管理.Global变量 ...
- PHP内核探索:哈希碰撞攻击是什么?
最近哈希表碰撞攻击(Hashtable collisions as DOS attack)的话题不断被提起,各种语言纷纷中招.本文结合PHP内核源码,聊一聊这种攻击的原理及实现. 哈希表碰撞攻击的基本 ...
- 24小时学通Linux内核--内核探索工具类
寒假闲下来了,可以尽情的做自己喜欢的事情,专心待在实验室里燥起来了,因为大二的时候接触过Linux,只是关于内核方面确实是不好懂,所以十天的时间里还是希望能够补充一下Linux内核相关知识,接下来继续 ...
- 十天学Linux内核之第一天---内核探索工具类
原文:十天学Linux内核之第一天---内核探索工具类 寒假闲下来了,可以尽情的做自己喜欢的事情,专心待在实验室里燥起来了,因为大二的时候接触过Linux,只是关于内核方面确实是不好懂,所以十天的时间 ...
- PHP服务器脚本 PHP内核探索:新垃圾回收机制说明
在5.2及更早版本的PHP中,没有专门的垃圾回收器GC(Garbage Collection),引擎在判断一个变量空间是否能够被释放的时候是依据这个变量的zval的refcount的值,如果refco ...
- 《PHP内核探索系列文章》系列分享专栏
<PHP内核探索系列文章>已整理成PDF文档,点击可直接下载至本地查阅 简介 PHP内核探索系列文章收藏夹收藏有关PHP内核方面的知识的文章,对PHP高级进阶的朋友提供PHP内核方面的知识 ...
- PHP内核探索之变量(7)- 不平凡的字符串
切,一个字符串有什么好研究的. 别这么说,看过<平凡的世界>么,平凡的字符串也可以有不平凡的故事.试看: (1) 在C语言中,strlen计算字符串的时间复杂度是?PHP中呢? ...
随机推荐
- libcurl 函数curl_easy_perform在release下崩溃的问题
今天遇到一个很奇怪的问题: 工程中用到了libcurl, debug可以正常运行,release每次都崩溃,断到curl_easy_perform这一行.堆栈中也得不到有用信息,于是GOOGLE一番, ...
- oracle分页查询sql
select * from( select shopid,rownum rn from p_shopinfo where is_hot=1 and rownum <=6 order by sho ...
- MVC后台数据赋值给前端JS对象
Controller中的数据,不管是使用的是ViewModel 还是ViewBag.Data,要将他传递到View中,这个很容易,但是如果要将它传递给JS中的某个对象,这个改如何处理呢? 后台的数据格 ...
- TCP的拥塞控制
1.引言 计算机网络中的带宽.交换结点中的缓存和处理机等,都是网络的资源.在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就会变坏.这种情况就叫做拥塞. 拥塞控制就是防止 ...
- hdu 5877 (dfs+树状数组) Weak Pair
题目:这里 题意: 给出一个n个结点的树和一个数k,每个结点都有一个权值,问有多少对点(u,v)满足u是v的祖先结点且二者的权值之积小于等于k. 从根结点开始dfs,假设搜的的点的权值是v,我们需要的 ...
- Java程序调用javascript等脚本的实现方法
public static void main(String[] args) throws FileNotFoundException, ScriptException, NoSuchMethodEx ...
- js基础到精通全面教程--JS教程
适合阅读范围:对JavaScript一无所知-离精通只差一步之遥的人 基础知识:HTML JavaScript就这么回事1:基础知识 1 创建脚本块 1: <script language=”J ...
- JavaScript基础知识整理(2)
15.处理图像 注意:(1)在写js文件时,尽量将函数的声明往后写,将函数调用写在前面,这样能够使代码结构很清晰. (2)一个网页中翻转器一般超过3个,所以使用for循环减少重复使用翻转器代码的次数. ...
- AngularJs自定义指令详解(4) - transclude
transclude默认值为false,如果设置 transclude为true,那么相应地,必须在模板代码中加入ng-transclude指令. 先看个例子: <!DOCTYPE html&g ...
- SQL Server已提交读快照隔离级别的设置
如果要把SQL Server数据库事务隔离级别设置为已提交读快照隔离 如果直接运行下面的语句: ALTER Database [mydbname] SET READ_COMMITTED_SNAPSHO ...