在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中呢? ...
随机推荐
- 反射——Java反射机制
反射概述 什么是反射? ① 反射的概念是由Smith在1982年首次提出的,主要指程序可以访问.检测和修改它本身状态或行为的一种能力. ② JAVA反射机制是在运行状态中,对应任意一个类,都能 ...
- 【splay模板】
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> ...
- arduino api手册
本文由博主原创,如有不对之处请指明,转载请说明出处. arduino 函数 api 程序结构 在Arduino中, 标准的程序入口main函数在内部被定义, 用户只需要关心以下两个函数:void se ...
- include使用中注意的问题
发现include文件不必添加,但是一定要放在工程路径下,不然就找不到,或者也可以在文件上写出完整的路径. 可以修改路径为'include "../src/enc_defines.v&quo ...
- 未能加载文件或程序集EntityFramework, Version=6.0.0.0解决办法
其他信息: 未能加载文件或程序集"EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934 ...
- android studio serialversionuid设置
- xlrd读取多个excel电子表数据
import sys import xlrd import traceback def ReadData(FileName): try: workBook = xlrd.open_workbook(F ...
- typedef 与指针、多维数组
1.在typedef中使用指针往往会带来意外的结果.如下: typedef string *pstring; const pstring cstr; 绝大数人刚开始都会认为cstr是一种指针,它指向c ...
- CSS换行文本溢出显示省略号,多行
首先,div部分 <body> <div>多行的显示该如何解决呢,后面经过一番google后,我找到了chrome的一个API可以解决上面提到的需求-webkit-line-c ...
- python模拟登陆知乎并爬取数据
一些废话 看了一眼上一篇日志的时间 已然是5个月前的事情了 不禁感叹光阴荏苒其实就是我懒 几周前心血来潮想到用爬虫爬些东西 于是先后先重写了以前写过的求绩点代码 爬了草榴贴图,妹子图网,后来想爬婚恋网 ...