開扒php內核函數,第三篇 implode

一開始以爲implode挺容易實現,可是寫着寫着才發現是挺複雜的,不說啦php

來看看implode的用法吧git

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 }

 

先說說思路吧app

1,主要是算法是2進制轉10進制 字符串顯示,固然咱們要注意負數啦,還有字符串翻轉ide

2 內存分配,由於咱們沒有限制數組的長度,因此咱們要動態去分配,其實咱們能夠有同樣能夠肯定的是整形的範圍 0到65535 就是說一個整形最多佔5個字符,函數

3 其餘就沒什麼啦oop

 

來看看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你們能夠用上面的方法去研究下

相關文章
相關標籤/搜索