开扒php内核函数,第三篇 implode
一开始觉得implode挺容易实现,但是写着写着才发现是挺复杂的,不说啦
来看看implode的用法吧
<?php
$arr = array('Hello','World!','Beautiful','Day!');
echo implode(" ",$arr);
?>
上面会输出 Hello World! Beautiful Day!
下面的程序的我写的
/*字符串翻转*/
void strover(char * str){
int len = strlen(str);
//int half = strlen(str)/2;
int i,j;
char tmp;
j = len-;
for(i=;i<=j;i++){
tmp = str[j];
str[j] = str[i];
str[i] = tmp;
j--;
} } /*
2进制转十进制 要处理正负数啊 涉及到负数啊
字符串翻转
*/
char * bin2decimal(int number){ int q = ; //商
int r = ;//余数
int i = ;
int tmp = number;
int is_negative = ;
char * res;
res = (char *)malloc(sizeof(char)*+);
if(number>=){ }else{
tmp = -number;
res[i++] = '-';
is_negative = ;
} do{
q = tmp/; r = tmp%;
// tmp = q;
// c = hex_str[r];
res[i++] = ''+r;
tmp = q;
}while(tmp); res[i] = '\0'; strover(&res[is_negative]);
return res; } /*
c语言真的太麻烦啦,传数组,但是无法知道数组的长度,只能够手动传入
*/
char * implode(int *number,int size,char * dem){
int i = ;
char* c;
//c[1] = '\0';
struct simple_mem{
char * res;
unsigned int len;
unsigned int used;
}test_mem;
test_mem.res = (char *)malloc(sizeof(char)*);
test_mem.len = sizeof(char)*;
test_mem.used = ;
for(;i<size;){
c= bin2decimal(number[i]);
memcpy(test_mem.res+test_mem.used,c,strlen(c)); test_mem.used+=strlen(c);
if(++i<size){
memcpy(test_mem.res+test_mem.used,dem,strlen(dem));
test_mem.used+=strlen(dem);
} }
test_mem.res[test_mem.used] = '\0';
printf("%s",test_mem.res); }
我们写的implode写的函数是针对整形数组,php的当然什么类型都支持啊,c语言也可以实现泛型,但毕竟比较麻烦的,上面的程序还是比较多问题的,优化的地方有很多,但是我们是抱着学习的态度来的
int main(){
//char * res = bin2hex("a");
//printf("hex a=%s",res);
//char * res = hex2bin("6578616d706c65206865782064617461");
int integer[] = {,-,};
implode(integer,sizeof(integer)/sizeof(int),"*"); //bin2decimal(-1234); return ;
}
先说说思路吧
1,主要是算法是2进制转10进制 字符串显示,当然我们要注意负数啦,还有字符串翻转
2 内存分配,因为我们没有限制数组的长度,所以我们要动态去分配,其实我们可以有一样可以确定的是整形的范围 0到65535 就是说一个整形最多占5个字符,
3 其他就没什么啦
来看看php的吧
/*
* Convert num to its decimal format.
* Return value:
* - a pointer to a string containing the number (no sign)
* - len contains the length of the string
* - is_negative is set to TRUE or FALSE depending on the sign
* of the number (always set to FALSE if is_unsigned is TRUE)
*
* The caller provides a buffer for the string: that is the buf_end argument
* which is a pointer to the END of the buffer + 1 (i.e. if the buffer
* is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
*/
/* char * ap_php_conv_10() {{{ */
char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned,
register bool_int * is_negative, char *buf_end, register int *len)
{
register char *p = buf_end;
register u_wide_int magnitude; if (is_unsigned) {
magnitude = (u_wide_int) num;
*is_negative = FALSE;
} else {
*is_negative = (num < ); /*
* On a 2's complement machine, negating the most negative integer
* results in a number that cannot be represented as a signed integer.
* Here is what we do to obtain the number's magnitude:
* a. add 1 to the number
* b. negate it (becomes positive)
* c. convert it to unsigned
* d. add 1
*/
if (*is_negative) {
wide_int t = num + ;
magnitude = ((u_wide_int) - t) + ;
} else {
magnitude = (u_wide_int) num;
}
} /*
* We use a do-while loop so that we write at least 1 digit
*/
do {
register u_wide_int new_magnitude = magnitude / ; *--p = (char)(magnitude - new_magnitude * + '');
magnitude = new_magnitude;
}
while (magnitude); *len = buf_end - p;
return (p);
}
> php5ts_debug.dll!ap_php_conv_10(__int64 num=-278, int is_unsigned=0, int * is_negative=0x00c3e154, char * buf_end=0x00c3e9c0, int * len=0x00c3ea64) 行320 C
php5ts_debug.dll!format_converter(buf_area * odp=0x00c3eb9c, const char * fmt=0x105d799e, char * ap=0x00c3ecc0) 行869 + 0x34 字节 C
php5ts_debug.dll!strx_printv(int * ccp=0x00c3eca0, char * buf=0x00c3ee90, unsigned int len=12, const char * format=0x105d799c, char * ap=0x00c3ecbc) 行1213 + 0x11 字节 C
php5ts_debug.dll!ap_php_slprintf(char * buf=0x00c3ee90, unsigned int len=12, const char * format=0x105d799c, ...) 行1229 + 0x19 字节 C
php5ts_debug.dll!php_implode(_zval_struct * delim=0x030dffd8, _zval_struct * arr=0x030dff88, _zval_struct * return_value=0x030e0028, void * * * tsrm_ls=0x00353040) 行1154 + 0x1b 字节 C
php5ts_debug.dll!zif_implode(int ht=2, _zval_struct * return_value=0x030e0028, _zval_struct * * return_value_ptr=0x00000000, _zval_struct * this_ptr=0x00000000, int return_value_used=1, void * * * tsrm_ls=0x00353040) 行1250 + 0x15 字节 C
php5ts_debug.dll!zend_do_fcall_common_helper_SPEC(_zend_execute_data * execute_data=0x030c20d8, void * * * tsrm_ls=0x00353040) 行643 + 0x62 字节 C
php5ts_debug.dll!ZEND_DO_FCALL_SPEC_CONST_HANDLER(_zend_execute_data * execute_data=0x030c20d8, void * * * tsrm_ls=0x00353040) 行2234 C
php5ts_debug.dll!execute(_zend_op_array * op_array=0x030dfa40, void * * * tsrm_ls=0x00353040) 行410 + 0x11 字节 C
php5ts_debug.dll!zend_execute_scripts(int type=8, void * * * tsrm_ls=0x00353040, _zval_struct * * retval=0x00000000, int file_count=3, ...) 行1329 + 0x21 字节 C
php5ts_debug.dll!php_execute_script(_zend_file_handle * primary_file=0x00c3fcf4, void * * * tsrm_ls=0x00353040) 行2502 + 0x1b 字节 C
php.exe!do_cli(int argc=2, char * * argv=0x00352fa0, void * * * tsrm_ls=0x00353040) 行989 + 0x10 字节 C
php.exe!main(int argc=2, char * * argv=0x00352fa0) 行1365 + 0x11 字节 C
调用堆栈如上
do {
register u_wide_int new_magnitude = magnitude / ; *--p = (char)(magnitude - new_magnitude * + '');
magnitude = new_magnitude;
}
while (magnitude);
关键是这段代码,作者没有像我们 用取余去计算,而是 把它乘,举个例子吧
magnitude = 283
new_magnitude = 283/10 = 28
*--p = 283 - 28*10+'0' = '3'
magnitude = new_magnitude = 28
然后继续上面的步骤啦
取余考虑和乘法考虑那个高,不知道作者的想法是怎样的,有时间用汇编证明一下,那个用的指令比较多
第二个的就是 作者用了倒序字符复制 *--p,这就要读读内存的代码啦
*len = buf_end - p; 字符串长度可以这样计算的,指针的作用就是不错啊
回到调用的地方如下
s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
&num_buf[NUM_BUF_SIZE], &s_len);
FIX_PRECISION(adjust_precision, precision, s, s_len); if (*fmt != 'u') {
if (is_negative) {
prefix_char = '-';
} else if (print_sign) {
prefix_char = '+';
} else if (print_blank) {
prefix_char = ' ';
}
}
break;
num_buf[NUM_BUF_SIZE] 这个东西长度为2048,不知道为什么要分配这么多的内存 上面的判断就是看看是不是负数,然后就 赋给修饰符
if (prefix_char != NUL) {
*--s = prefix_char;
s_len++;
}
应该很容易吧
下面来看下一层的调用
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **) &tmp, &pos) == SUCCESS) {
switch ((*tmp)->type) {
case IS_STRING:
smart_str_appendl(&implstr, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
break; case IS_LONG: {
char stmp[MAX_LENGTH_OF_LONG + ];
str_len = slprintf(stmp, sizeof(stmp), "%ld", Z_LVAL_PP(tmp));
smart_str_appendl(&implstr, stmp, str_len);
}
break; case IS_BOOL:
if (Z_LVAL_PP(tmp) == ) {
smart_str_appendl(&implstr, "", sizeof("")-);
}
break; case IS_NULL:
break; case IS_DOUBLE: {
char *stmp;
str_len = spprintf(&stmp, , "%.*G", (int) EG(precision), Z_DVAL_PP(tmp));
smart_str_appendl(&implstr, stmp, str_len);
efree(stmp);
}
break; case IS_OBJECT: {
int copy;
zval expr;
zend_make_printable_zval(*tmp, &expr, ©);
smart_str_appendl(&implstr, Z_STRVAL(expr), Z_STRLEN(expr));
if (copy) {
zval_dtor(&expr);
}
}
break; default:
tmp_val = **tmp;
zval_copy_ctor(&tmp_val);
convert_to_string(&tmp_val);
smart_str_appendl(&implstr, Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
zval_dtor(&tmp_val);
break; }
我们是在这段代码
case IS_LONG: {
char stmp[MAX_LENGTH_OF_LONG + 1];
str_len = slprintf(stmp, sizeof(stmp), "%ld", Z_LVAL_PP(tmp));
smart_str_appendl(&implstr, stmp, str_len);
}
+ &implstr 0x00c3ef04 {c=0x030e0100 "1.5-" len=4 a=78 } smart_str *
+ stmp 0x00c3ee90 "-278" char [12]
str_len 4 int
+ tmp 0x030e0924 _zval_struct * *
php数字默认类型是长整形的,从上面可知道,stmp="-278",strlen = 4,
我们接下来看看implstr是这样处理的,首先他的结构是这样的
typedef struct {
char *c; 指向一段内存
size_t len; 已经用了多小
size_t a; 总共有多小
} smart_str;
smart_str_appendl 的定义是这样的
#define smart_str_appendl_ex(dest, src, nlen, what) do { \
register size_t __nl; \
smart_str *__dest = (smart_str *) (dest); \
\
smart_str_alloc4(__dest, (nlen), (what), __nl); \
memcpy(__dest->c + __dest->len, (src), (nlen)); \
__dest->len = __nl; \
} while ()
复制字符串用了memcpy
smart_str_alloc4这个定义如下
#define smart_str_alloc4(d, n, what, newlen) do { \
if (!(d)->c) { \
(d)->len = ; \
newlen = (n); \
(d)->a = newlen < SMART_STR_START_SIZE \
? SMART_STR_START_SIZE \
: newlen + SMART_STR_PREALLOC; \
SMART_STR_DO_REALLOC(d, what); \
} else { \
newlen = (d)->len + (n); \
if (newlen >= (d)->a) { \
(d)->a = newlen + SMART_STR_PREALLOC; \
SMART_STR_DO_REALLOC(d, what); \
} \
} \
} while ()
这个很清楚啦流程啦
如果implstr 没有分配过的,那么闲分配一段内存
如果implstr分配过,并且当前的空间不够容纳新的字符 在这基础上扩展啦 SMART_STR_PREALLOC =78 不知道为什么是78
看下定义吧
#define SMART_STR_DO_REALLOC(d, what) \
(d)->c = SMART_STR_REALLOC((d)->c, (d)->a + , (what))
#define SMART_STR_REALLOC(a,b,c) perealloc((a),(b),(c))
#define perealloc(ptr, size, persistent) ((persistent)?__zend_realloc((ptr), (size)):erealloc((ptr), (size)))
inline static void * __zend_realloc(void *p, size_t len)
{
p = realloc(p, len);
if (p) {
return p;
}
fprintf(stderr, "Out of memory\n");
exit();
}
最终是调用了 c语言的realloc函数,这样就大概明白了吧
到最后加上分割符号
if (++i != numelems) {
smart_str_appendl(&implstr, Z_STRVAL_P(delim), Z_STRLEN_P(delim));
}
上面是分析了整数的implode,至于浮点数,对象,字符串 的implode大家可以用上面的方法去研究下
开扒php内核函数,第三篇 implode的更多相关文章
- 开扒php内核函数,第二篇 hex2bin
从上一篇我们得知怎样把ascii变成16进制显示,这篇我们是怎样把16进制变成ascii显示 我们还是从分析开始吧 先看这个函数的介绍吧 string hex2bin ( string $data ) ...
- 开扒php内核函数,第一篇 bin2hex
这段时间真的比较有时间,所以自己用c写一下bin2hex啦 写个php的人都知道,这是个比较熟悉的函数吧,没有什么高深,只是把输入的东西以16进制输出吧了 先分析一下,这个函数要怎么写吧,他会有一定的 ...
- Python之函数第三篇
一.匿名函数 匿名函数是lambda定义的没有名字的具有一些小功能的函数 具体形式是 lambda x: X**2 # 求平方操作 lambda x: x>100 # 判断参数是否大于100 二 ...
- 深入理解javascript函数系列第三篇——属性和方法
× 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...
- python成长之路第三篇(1)_初识函数
目录: 函数 为什么要使用函数 什么是函数 函数的返回值 文档化函数 函数传参数 文件操作(二) 1.文件操作的步骤 2.文件的内置方法 函数: 一.为什么要使用函数 在日常写代码中,我们会发现有很多 ...
- 深入理解javascript函数系列第三篇
前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解javascript函数 ...
- python 【第三篇】:函数及参数
函数背景 在学习函数之前,一直遵循:面向过程编程: 根据业务逻辑从上到下实现功能,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,也就是将之前实现的代码块复制到现需功能处,如下: ...
- 第三篇:python函数
1.python函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你 ...
- 关于Linux系统调用,内核函数【转】
转自:http://blog.csdn.net/ubuntulover/article/details/5988220 早上听人说到某个程序的一部分是内核态,另一部分是用户态,需要怎么怎么.当时突然想 ...
随机推荐
- 京东商城发现了一枚Bug
我在京东上买了几本书,发现了一个BUG.. 买书的时候,我选了京东自营的书和京东其他店的书,合在一起购买,填写了开具发票. 然后,京东处理流程是,将上面一笔订单拆分成两笔,然后发票信息没有转到其他店那 ...
- 在Visual C++下搭建OpenGL的开发环境
1.确保你的电脑已经安装了visual c++编译器 如果还没安装的话,这里有个安装包,可以复制链接进行下载:http://pan.baidu.com/s/1bn4XTqn 2.下载GLUT 下载 ...
- Python 学习记录
记录一些 学习python 的过程 -------------------------------------- 1. 初始学习 @2013年10月6日 今天开始学习python 了 遇到好多困难但是 ...
- CF-358D-Dima and Hares【T^T+*^*】
[文章标题打着转载,是因为不是自己想出来的解题,但下面的文字是自己敲的[~捂脸*>_<*~]] 题目就不贴了~~~DP+greedy的题.弱爆了看别人的代码思路过的.T^T但还是自己复述一 ...
- 利用SOLR搭建企业搜索平台 之——配置文件
运行solr是个很简单的事,如何让solr高效运行你的项目,这个就不容易了.要考虑的因素太多.这里很重要一个就是对solr的配置要了解.懂得配置文件每个配置项的含义,这样操作起来就会如鱼得水! 在so ...
- G-sensor 与M-sensor区别
g-sensor是重力传感器,能感应芯片在三个方向(通常是)上的重力加速度.手机里的重力球用的就是这个技术,m-sensor如果是motion sensor的简称的话,基本上指的和g-sensor是一 ...
- [CCPC2016]网赛部分比赛代码
来自HDOJ: 5833 ( Zhu and 772002 ) /* ━━━━━┒ギリギリ♂ eye! ┓┏┓┏┓┃キリキリ♂ mind! ┛┗┛┗┛┃\○/ ┓┏┓┏┓┃ / ┛┗┛┗┛┃ノ) ┓┏ ...
- Hadoop集群(第9期)_MapReduce初级案例
1.数据去重 "数据去重"主要是为了掌握和利用并行化思想来对数据进行有意义的筛选.统计大数据集上的数据种类个数.从网站日志中计算访问地等这些看似庞杂的任务都会涉及数据去重.下面就 ...
- HDU 1072 (不一样的入队条件) Nightmare
之前的BFS都是需要一个标记数组,但这个题不一样,因为可能一个格子不止走一次. 那么我们就要寻找新的入队条件:left比上次经过的时候大才入队(left表示上次经过该点时剩余的时间). 为什么呢?我们 ...
- 设置sudo不输入密码 sudoers 编辑出错后的补救方法
一 设置sudo为不需要密码 有时候我们只需要执行一条root权限的命令也要su到root,是不是有些不方便?这时可以用sudo代替.默认新建的用户不在sudo组,需要编辑/etc/sudoers文件 ...