http://home.netcom.com/~tjensen/ptr/cpoint.htm

這裡有很多資料可以看 !!

先看以下的例子 :

int i = 10 ;

int *j = &i ;

printf("(%p)(%p)\n",j,&i) ;

得到的答案是 : (0022FEC0)(0022FEC0)

* 跟 & 像是 反矩陣的關係一樣 ,  *(&i)  其實就是 i  ,  *  是 dereference 的 操作 ,

int *j  的意思是 ,  j 是個指標變數 , 它的內容是一個放著 integer內容 的記憶體位址 ,

要去存取個記憶體內容 ,必須使用 * 這個方式  , 所以 , int *j = &i  ; 代表  j 是一個指標 ,

它的內容是一個放著 integer 內容的記憶體位址, 而那個位址是變數 i 的位址 !!!!!!

從 printf 看的到 ,  j and &i  是一樣的值 , *j 是去 存取 那個 integer 的值 ,

*(&i) 也是 , 既然 i = 10 , 所以 *j  以及 also *(&i)  都等於 10 了 !!

 

我們不能作以下的事 :  int *j ;   *j = 10 ;

這是因為 int *j  宣告  j 是一個指標變數 , 內容是一個 integer 內容的記憶體位址 , 如果你沒有使用

malloc 去取得合法記憶體 , *j = 10  這個指令 , 會根據 j 當時記憶體位址 把 10 放進去 !!

但是這個位址不是合法取得 , 你把 10 塞進去那塊記憶體會讓程式有當掉風險 !!

j = (int*) malloc(sizeof(int)) ;   會取得一塊 integer size 的記憶體, 把此記憶體位址填入 j  的內容 ;

*j  = 10  就是 從 j 內容的記憶體位址找出來 , 把 10 這個數字寫到那一塊記憶體進去 !!

 

以下是各種範例  !!

Case 1 :

int ptr[3][3] = {0,1,2,
                        3,4,5,
                        6,7,8} ;

printf("(%d)(%d)(%d)(%d)\n",*(*ptr),*(*ptr+1),*(*(ptr+1)),*(*(ptr+1)+1)  ) ;

得到 : (0)(1)(3)(4)

因為 指標的 dereference , *ptr 相當於 ptr[0]  , *(ptr+1) 相當於 ptr[1] ,

所以 , *(*ptr)  = *(ptr[0])  = ptr[0][0]  ...

*(*ptr+1) = *(ptr[0]+1) = ptr[0][1] ...

*(*(ptr+1)) = *(ptr[1]) = ptr[1][0] ...

*(*(ptr+1)+1) = *(ptr[1]+1) = ptr[1][1] ...

有點像是代數......

 

Case 2 :

int *m[3] ;
int idx,idy,width=5,height=3 ;
m[0] = (int*) malloc(width * height * sizeof(int)) ;

for(idx=0;idx<height*width;idx++)
{
     *(m[0] + idx) = idx ;
}

for(idx=1;idx<height;idx++)
{
     m[idx] = m[idx-1] + width ;
}


for(idx=0;idx<height;idx++)
{
     for(idy=0;idy<width;idy++)
     {
         printf("(%d)",m[idx][idy]) ;
     }
     printf("\n") ;
}

Ans :

(0)(1)(2)(3)(4)
(5)(6)(7)(8)(9)
(10)(11)(12)(13)(14)

 這個例子太簡單,不解釋 !!

Case 3 :

typedef struct node_
{
    int level ;
    int *ptr[1] ;
} node ;

 node* x ;
 x = (node*) malloc(sizeof(node) + 10 * sizeof(int*)) ;
 x->level = 10 ;
 for(idx=0;idx<=x->level;idx++)
 {
     printf("(%p)==>",&(x->ptr[idx]) ) ;
 } 

Ans :

(003E2514)==>(003E2518)==>(003E251C)==>(003E2520)==>(003E2524)==>(003E2528)==>(0
03E252C)==>(003E2530)==>(003E2534)==>(003E2538)==>(003E253C)==>

這個是很有用的指標用法, skip list 就是使用這個技巧,注意到 int *ptr[1]

這個語法是否怪怪的? 指標陣列的陣列長度為 1 ? 不就跟 int *ptr 一樣 ?

不一樣 !! 一個是指標陣列 , int *ptr 只是一個指標 ,

正常來說,我們在作  linked list 只需要 sizeof(node) 的記憶體,但是 skip list 需要有彈性高度的指標,

例如本例中的 10 ,  注意 , 這個用法一定要將 int* ptr[1] 放在結構最後面,不在最後一定錯 !!

還有 , 假如指標有 4 bytes 的話, x 指標所指的空間有 : 8 + 10 * 4 = 48 bytes !!

也就是說 ,  x 有 11 個 integer pointer , 不是 10 個喔 ,原先的 node 結構是一個 ,

後面我們多加 十個指標空間給 x , total 是 48 bytes 連續空間 ,本例中, 記憶體裡面有一個 integer level,

加上十一個指標 : ptr[0] ~~ ptr[10]  , 透過這種方法,可以操作彈性的指標陣列個數 !!

skip list 需要節省空間 , 必須彈性設定每個 node 高度 ,  這個指標陣列用法是 excellent solution !!

注意 , int *ptr[0]  是相同意思 ,  不過有些 compiler 會失敗 ! int *ptr[1] 則適用於每種 compiler !!

 

Case 4 :

typedef struct people_
{
    char sex[9]  ;
    int  age ;
    char *name ;
    char *phone[3] ;
} people ;

 people Mars  ;
 strcpy(Mars.sex,"Male") ;
 Mars.age = 10 ;
 Mars.name = (char*) malloc(20) ;
 strcpy(Mars.name,"Mars") ;
 Mars.phone[0]= (char*) malloc(20) ;
 Mars.phone[1]= (char*) malloc(20) ;
 Mars.phone[2]= (char*) malloc(20) ;
 strcpy(Mars.phone[0],"111-111-1111") ;
 strcpy(Mars.phone[1],"222-222-2222") ;
 strcpy(Mars.phone[2],"333-333-3333") ;
 
 size_t offset ;
 offset = offsetof(people,sex[0]) ;
 char *sex = (char *) ( (char *)(&Mars) + offset) ;
 printf("offset=(%d),sex=(%s)\n",offset,sex) ;
 
 offset = offsetof(people,age) ;
 int age = *(int *) ( (char *)(&Mars) + offset) ;
 printf("offset=(%d),age=(%d)\n",offset,age) ;
 
 offset = offsetof(people,name) ;
 char *name = *(char **) ( (char *)(&Mars) + offset) ;
 printf("offset=(%d),,name=(%s)\n",offset,name) ;
 
 int idx,idy ;
 char *phone ;
 for(idx=0;idx<3;idx++)
 {
     offset = offsetof(people,phone[idx]) ;
      phone = *(char **) ( (char *)(&Mars) + offset) ; 
      printf("offset=(%d),,phone=(%s)\n",offset,phone) ;
 } //for

Ans :

offset=(0),sex=(Male)
offset=(12),age=(10)
offset=(16),,name=(Mars)
offset=(20),,phone=(111-111-1111)
offset=(24),,phone=(222-222-2222)
offset=(28),,phone=(333-333-3333)

offsetof return 第二個參數在  structure 裡面的 offset ,  age offset = 12 , not 10 ,

因為 memory alignment 讓 compiler 強迫每個變數的記憶體位址都必須是其變數長度的倍數 ,

integer 長度是 4 , 所以其記憶體必須 locate 在可以整除於 4 的位址 !! compiler 在 sex and age

中間塞了 2 個 byte 沒用的空間 !!

想一個問題 , 在 intel cpu 架構 , 一條 cache line 有 64 bytes !! 如果 一個變數沒有這樣調整的話,

你可能有一個 integer 變數 , 其前兩個 bytes 在 x cache line 最尾巴兩個空間, 後兩個 bytes

在另一個 y cache line 的最前面 , 此時 , concurrency 如何作 ? 你只能用 lock ,

integer 在同一個 cache line 時 , 同時間有讀有寫的 concurrency 對它不是問題 ,

可以做 atomic 存取, 這就是 compiler 對變數作 memory alignment 的原因  !!!

 

char *sex = (char *) ( (char *)(&Mars) + offset) ;

&Mars 是 structure 記憶體所在 , (char *)(&Mars) 把它變成一個  char * 的指標 ,

我舉個例子為啥這樣做 :

int* px = (int*) malloc(10 * sizeof(int)) ; 

char* py = (char*) malloc(10 * sizeof(char)) ; 

px所指的 integer 空間  跟 (px+1) 所指的 integer 空間 記憶體相差 4 bytes(如果 sizeof(int) = 4 的話) ,

py所指的 char 空間  跟 (px+1) 所指的 char 空間 記憶體相差 1 byte  only !!!

所以,  ( (char *)(&Mars) + offset)  可以順利得到從 &Mars 移動 offset bytes 空間的記憶體位址 !!

把這記憶體位址給 sex , 工作就完成了 !!!

 

offset = offsetof(people,age) ;

int age = *(int *) ( (char *)(&Mars) + offset) ;

這裡舉個例子 :

int ix = 10 ;

int iy = *(&ix) ; // iy = 10 , of course

從任何一個變數記憶體 dereference 出來的 , 就是其變數的值 !! 也就是說 , ix = *(&ix) ,

也就是說 , *& = 1 , 所以 ,

int age = *(int *) ( (char *)(&Mars) + offset) ;

把這個 ( (char *)(&Mars) + offset) address dereference 成 int , 就這樣而已 !!!

 

 int idx,idy ;
 char *phone ;
 for(idx=0;idx<3;idx++)
 {
     offset = offsetof(people,phone[idx]) ;
      phone = *(char **) ( (char *)(&Mars) + offset) ; 
      printf("offset=(%d),,phone=(%s)\n",offset,phone) ;
 } //for

 

phone = *(char **) ( (char *)(&Mars) + offset) ; 

( (char *)(&Mars) + offset) 這個 address 被 dereference 出來是

phone[0] or phone[1] or phone[2] , 也就是說 , 這個 address 內容放的是指標 !!!

舉個例子 :

char *ps1,*ps2 ;
ps1 = malloc(10) ;
strcpy(ps1,"12345");
ps2 = *(char**)(&ps1) ;
printf("(%s)\n",ps2) ; 

這個例子可以清楚解釋我的想法 !!

int *ps1,*ps2 ;

ps1 = ps2 ;

printf("(%p)(%p)\n",ps1,ps2) ;

address of ps1 and address of ps2 is different ,

但是它們內容相同 , called by pointer 就是這個觀念 !!!!

 

 

 

 

 

 

 

 

 

arrow
arrow
    全站熱搜

    hedgezzz 發表在 痞客邦 留言(0) 人氣()