开扒php内核函数,第三篇 implode

一开始觉得implode挺容易实现,但是写着写着才发现是挺复杂的,不说啦

来看看implode的用法吧

1 <?php

2 $arr = array('Hello','World!','Beautiful','Day!');

3 echo implode(" ",$arr);

4 ?>
上面会输出 Hello World! Beautiful Day!

下面的程序的我写的

 1 /*字符串翻转*/

 2 void strover(char * str){

 3     int len = strlen(str);

 4     //int half =  strlen(str)/2;

 5     int i,j;

 6     char tmp;

 7     j = len-1;

 8     for(i=0;i<=j;i++){

 9         tmp = str[j];

10         str[j] = str[i];

11         str[i] = tmp;

12         j--;

13     }

14     

15 

16 }

17 

18 

19 

20 

21 

22 /*

23     2进制转十进制 要处理正负数啊 涉及到负数啊

24     字符串翻转

25 */

26 char * bin2decimal(int number){

27    

28    int q = 0; //

29     int r = 0;//余数

30     int i  = 0;

31     int tmp = number;

32   int is_negative = 0;

33     char * res;

34      res = (char *)malloc(sizeof(char)*5+1);

35    if(number>=0){

36       

37    }else{

38         tmp = -number;

39         res[i++] = '-';

40         is_negative = 1;

41    }

42   

43          do{

44           q = tmp/10;

45 

46           r  = tmp%10;

47          // tmp = q;

48          // c = hex_str[r];

49          res[i++] = '0'+r;

50         tmp = q;

51        }while(tmp);

52    

53    res[i] = '\0';

54   

55     strover(&res[is_negative]);

56   return res;

57    

58 

59 }

60 

61 

62 

63 

64 /*

65     c语言真的太麻烦啦,传数组,但是无法知道数组的长度,只能够手动传入

66 */

67 char * implode(int *number,int size,char * dem){

68     int i = 0;

69     char* c;

70     //c[1] = '\0';

71     struct simple_mem{

72         char * res;

73         unsigned int len;

74         unsigned int used;

75     }test_mem;

76     test_mem.res = (char *)malloc(sizeof(char)*20);

77     test_mem.len = sizeof(char)*20;

78     test_mem.used = 0;

79     for(;i<size;){

80              c= bin2decimal(number[i]);

81            memcpy(test_mem.res+test_mem.used,c,strlen(c));

82           

83             test_mem.used+=strlen(c);

84             if(++i<size){

85                  memcpy(test_mem.res+test_mem.used,dem,strlen(dem));

86                  test_mem.used+=strlen(dem);

87             }

88             

89     }

90  test_mem.res[test_mem.used] = '\0';

91  printf("%s",test_mem.res);

92 

93 

94 }

我们写的implode写的函数是针对整形数组,php的当然什么类型都支持啊,c语言也可以实现泛型,但毕竟比较麻烦的,上面的程序还是比较多问题的,优化的地方有很多,但是我们是抱着学习的态度来的

 1 int main(){

 2     //char * res = bin2hex("a");

 3     //printf("hex a=%s",res);

 4     //char * res = hex2bin("6578616d706c65206865782064617461");

 5     int integer[3] = {1,-24,3};

 6     implode(integer,sizeof(integer)/sizeof(int),"*");

 7 

 8     //bin2decimal(-1234);

 9     

10     return 0;

11 }

 

先说说思路吧

1,主要是算法是2进制转10进制 字符串显示,当然我们要注意负数啦,还有字符串翻转

2 内存分配,因为我们没有限制数组的长度,所以我们要动态去分配,其实我们可以有一样可以确定的是整形的范围 0到65535 就是说一个整形最多占5个字符,

3 其他就没什么啦

 

来看看php的吧

 1 /*

 2  * Convert num to its decimal format.

 3  * Return value:

 4  *   - a pointer to a string containing the number (no sign)

 5  *   - len contains the length of the string

 6  *   - is_negative is set to TRUE or FALSE depending on the sign

 7  *     of the number (always set to FALSE if is_unsigned is TRUE)

 8  *

 9  * The caller provides a buffer for the string: that is the buf_end argument

10  * which is a pointer to the END of the buffer + 1 (i.e. if the buffer

11  * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])

12  */

13 /* char * ap_php_conv_10() {{{ */

14 char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned,

15        register bool_int * is_negative, char *buf_end, register int *len)

16 {

17     register char *p = buf_end;

18     register u_wide_int magnitude;

19 

20     if (is_unsigned) {

21         magnitude = (u_wide_int) num;

22         *is_negative = FALSE;

23     } else {

24         *is_negative = (num < 0);

25 

26         /*

27          * On a 2's complement machine, negating the most negative integer

28          * results in a number that cannot be represented as a signed integer.

29          * Here is what we do to obtain the number's magnitude:

30          *      a. add 1 to the number

31          *      b. negate it (becomes positive)

32          *      c. convert it to unsigned

33          *      d. add 1

34          */

35         if (*is_negative) {

36             wide_int t = num + 1;

37             magnitude = ((u_wide_int) - t) + 1;

38         } else {

39             magnitude = (u_wide_int) num;

40         }

41     }

42 

43     /*

44      * We use a do-while loop so that we write at least 1 digit

45      */

46     do {

47         register u_wide_int new_magnitude = magnitude / 10;

48 

49         *--p = (char)(magnitude - new_magnitude * 10 + '0');

50         magnitude = new_magnitude;

51     }

52     while (magnitude);

53 

54     *len = buf_end - p;

55     return (p);

56 }

> 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

调用堆栈如上

 

1  do {

2          register u_wide_int new_magnitude = magnitude / 10;

3  

4          *--p = (char)(magnitude - new_magnitude * 10 + '0');

5          magnitude = new_magnitude;

6      }

7      while (magnitude);

 

关键是这段代码,作者没有像我们 用取余去计算,而是 把它乘,举个例子吧

magnitude = 283

new_magnitude = 283/10 = 28

*--p = 283 - 28*10+'0' = '3'

magnitude = new_magnitude = 28

然后继续上面的步骤啦

取余考虑和乘法考虑那个高,不知道作者的想法是怎样的,有时间用汇编证明一下,那个用的指令比较多

第二个的就是 作者用了倒序字符复制 *--p,这就要读读内存的代码啦

*len = buf_end - p; 字符串长度可以这样计算的,指针的作用就是不错啊

回到调用的地方如下

 1     s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,

 2                                 &num_buf[NUM_BUF_SIZE], &s_len);

 3                     FIX_PRECISION(adjust_precision, precision, s, s_len);

 4 

 5                     if (*fmt != 'u') {

 6                         if (is_negative) {

 7                             prefix_char = '-';

 8                         } else if (print_sign) {

 9                             prefix_char = '+';

10                         } else if (print_blank) {

11                             prefix_char = ' ';

12                         }

13                     }

14                     break;

 

num_buf[NUM_BUF_SIZE] 这个东西长度为2048,不知道为什么要分配这么多的内存

上面的判断就是看看是不是负数,然后就 赋给修饰符

	        if (prefix_char != NUL) {

				*--s = prefix_char;

				s_len++;

			}

应该很容易吧

下面来看下一层的调用

 1 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **) &tmp, &pos) == SUCCESS) {

 2         switch ((*tmp)->type) {

 3             case IS_STRING:

 4                 smart_str_appendl(&implstr, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));

 5                 break;

 6 

 7             case IS_LONG: {

 8                 char stmp[MAX_LENGTH_OF_LONG + 1];

 9                 str_len = slprintf(stmp, sizeof(stmp), "%ld", Z_LVAL_PP(tmp));

10                 smart_str_appendl(&implstr, stmp, str_len);

11             }

12                 break;

13 

14             case IS_BOOL:

15                 if (Z_LVAL_PP(tmp) == 1) {

16                     smart_str_appendl(&implstr, "1", sizeof("1")-1);

17                 }

18                 break;

19 

20             case IS_NULL:

21                 break;

22 

23             case IS_DOUBLE: {

24                 char *stmp;

25                 str_len = spprintf(&stmp, 0, "%.*G", (int) EG(precision), Z_DVAL_PP(tmp));

26                 smart_str_appendl(&implstr, stmp, str_len);

27                 efree(stmp);

28             }

29                 break;

30 

31             case IS_OBJECT: {

32                 int copy;

33                 zval expr;

34                 zend_make_printable_zval(*tmp, &expr, &copy);

35                 smart_str_appendl(&implstr, Z_STRVAL(expr), Z_STRLEN(expr));

36                 if (copy) {

37                     zval_dtor(&expr);

38                 }

39             }

40                 break;

41 

42             default:

43                 tmp_val = **tmp;

44                 zval_copy_ctor(&tmp_val);

45                 convert_to_string(&tmp_val);

46                 smart_str_appendl(&implstr, Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));

47                 zval_dtor(&tmp_val);

48                 break;

49 

50         }

 

我们是在这段代码

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是这样处理的,首先他的结构是这样的

1 typedef struct {

2     char *c; 指向一段内存

3     size_t len; 已经用了多小

4     size_t a; 总共有多小

5 } smart_str;
smart_str_appendl 的定义是这样的

1 #define smart_str_appendl_ex(dest, src, nlen, what) do {            \

2     register size_t __nl;                                            \

3     smart_str *__dest = (smart_str *) (dest);                        \

4                                                                     \

5     smart_str_alloc4(__dest, (nlen), (what), __nl);                    \

6     memcpy(__dest->c + __dest->len, (src), (nlen));                    \

7     __dest->len = __nl;                                                \

8 } while (0)

复制字符串用了memcpy

smart_str_alloc4这个定义如下

 

 1 #define smart_str_alloc4(d, n, what, newlen) do {                    \

 2     if (!(d)->c) {                                                    \

 3         (d)->len = 0;                                                \

 4         newlen = (n);                                                \

 5         (d)->a = newlen < SMART_STR_START_SIZE                         \

 6                 ? SMART_STR_START_SIZE                                 \

 7                 : newlen + SMART_STR_PREALLOC;                        \

 8         SMART_STR_DO_REALLOC(d, what);                                \

 9     } else {                                                        \

10         newlen = (d)->len + (n);                                    \

11         if (newlen >= (d)->a) {                                        \

12             (d)->a = newlen + SMART_STR_PREALLOC;                    \

13             SMART_STR_DO_REALLOC(d, what);                            \

14         }                                                            \

15     }                                                                \

16 } while (0)

这个很清楚啦流程啦

如果implstr 没有分配过的,那么闲分配一段内存

如果implstr分配过,并且当前的空间不够容纳新的字符 在这基础上扩展啦  SMART_STR_PREALLOC =78 不知道为什么是78

看下定义吧

 1 #define SMART_STR_DO_REALLOC(d, what) \

2 (d)->c = SMART_STR_REALLOC((d)->c, (d)->a + 1, (what)) 

 1 #define SMART_STR_REALLOC(a,b,c) perealloc((a),(b),(c)) 

 1 #define perealloc(ptr, size, persistent) ((persistent)?__zend_realloc((ptr), (size)):erealloc((ptr), (size))) 

1 inline static void * __zend_realloc(void *p, size_t len)

2 {

3     p = realloc(p, len);

4     if (p) {

5         return p;

6     }

7     fprintf(stderr, "Out of memory\n");

8     exit(1);

9 }

最终是调用了 c语言的realloc函数,这样就大概明白了吧

到最后加上分割符号

 1 if (++i != numelems) {

2 smart_str_appendl(&implstr, Z_STRVAL_P(delim), Z_STRLEN_P(delim));

3 } 

上面是分析了整数的implode,至于浮点数,对象,字符串 的implode大家可以用上面的方法去研究下

 

你可能感兴趣的:(PHP)