標籤

2013年9月22日 星期日

stack-push & pop

typedef struct node {
   int data;
   struct node *next;
}node;

// TOP of a stack
node *top = NULL;

// push a value into a stack
void push(int val)
{
   // allocate a new node for val
   node *new_node =(node*) malloc(sizeof(node));
   new_node->data = val;
   new_node->next = top;
   top = new_node;
}
// pop a value out from the stack
int pop(void)
{
   // tmp for record the top node
   node *tmp;
   int return_value;
   // if there is an existing data in stack
   if (top != NULL) {
      // get the value of the top
      return_value = top->data;
      // record the top node
      tmp = top;
      // change top
      top = top->next;
      free(tmp);
      return return_value;
   }
   else
      printf("no existing data in the stack!\n");
}

// show the stack
void show_stack(void)
{
   node *rush = top;
   printf("\nthe stack is:\n");
   while (rush != NULL) {
      printf("%d ", rush->data);
      rush = rush->next;
   }
   puts("");
}
int main()
{
   printf("Enter values:\n");
   int n;
   while (scanf("%d", &n) != EOF) {
      push(n);
      printf("%d ",n);
   }
   puts("");

   show_stack();
   pop();
   show_stack();
   pop();
   show_stack();

   return 0;
}
堆疊顧名思義就如同一疊一疊很重的貨物,擺放的時候當然是從下往上疊,拿的時候就從上面拿到下面。這是最順手的方式。這種資料的存取就稱為是Last In First Out。在底層的角度來說,compiler在處理function也是如此實做,詳情也可參考計算機組織結構。os會在記憶體之中規畫一個stack區域,專門記錄函式的返回位置。

比如一個主程式位址從0~100。中間有個function A位於49,那麼在執行完這function A的時候就需要回到第50這個位置,但是有可能function內又有一個function,於是我們不僅要把50放入stack先做儲存,也必須把function A內另一個function返回位置也儲存下來。但是先後順序當然是越裡面的位置要先要拿出來返回,於是最後放入的(Last In)位置就當然是會優先返回(First Out)。這就是stack。

當然實做上不是只有使用stack來當作function的返回記錄,以現今高速處理的計算機而言,要每次去access記憶體也是需要花很多時間的,因此目前採用的做法都是直接放入暫存器內而不放在記憶體中。另一種方式就是把返回位址寫在程式內,也就是直接在一個function的結尾多上一個jp (addr)的指令,這種方式就不用堆疊了。

題外話不多說,基本上他和linked list是差不多的架構,唯一不同的就是連接起來的方式,新加入的資料永遠都會擺在頭的位置。
void push(int val)
{
   // allocate a new node for val
   node *new_node =(node*) malloc(sizeof(node));
   new_node->data = val;
   new_node->next = top;
   top = new_node;
}
首先create資料位置並輸入資料之後,我們就把資料擺在top之上,最後重新指定top,這就是堆疊放入新資料的做法:top總是指向最後放入的資料。這個function的做法應該一目了然。

相對於push是把資料疊放在top;pop採取的做法就是把top上的資料移下去,後進先出(LIFO)就是如此。

  if (top != NULL){
     ...
  }
  else
    printf("no existing data in the stack!\n");
首先我們要判斷目前是否有資料在stack內,判斷的方式很簡單,從top就可以看出是否有資料在內。如果沒有就印出訊息提醒。而判斷如果內有資料後,我們便需要把資料拿取出來:
      return_value = top->data;
      // record the top node
      tmp = top;
      // change top
      top = top->next;
      free(tmp);
      return return_value;
因為top在取出資料需要往下移動,因此需要一個暫時的指標tmp來儲放原先top的位置用來最後free()的動作。因此return_value取出資料之後,隨即記錄top的位置,接著top往下移動之後在free掉原先top的位置(也就是tmp)。要注意的是順序要對,如果沒有把top的位置記錄起來而往下移動的話,那麼到時候必定是出trouble的。最後return結果pop()便完成使命了。

2013年9月10日 星期二

link list-小專題-記憶體管理-free

// move the nth node out from occupied list
MemInfo *move_out(int n)
{
   MemInfo *rush = head_occupy;
   // get the nth node
   while (n != 0) {
      rush = rush->next;
      n--;
   }
   // prev->rush->next
   // prev->next
   if (rush->next != NULL)
      rush->next->prev = rush->prev;
   if (rush == head_occupy)
      head_occupy = rush->next;
   else
      rush->prev->next = rush->next;
   return rush;
}

// move node to free list
void free_(MemInfo *node)
{
   MemInfo *rush, *prev;
   // initialize rush as head
   rush = head_free;
   // find out the position where node should
   // be put in
   while (rush->begin < node->begin) {
      prev = rush;
      rush = rush->next;
   }
   // if this node should be put in the first place
   if (rush == head_free) {
      // if rush is right after node
      if (rush->begin == node->begin + node->size) {
         // then merge them together
         rush->begin = node->begin;
         rush->size += node->size;
      }
      // otherwise
      else {
         node->next = rush;
         node->prev = NULL;
         rush->prev = node;
         head_free = node;
      }
   }
   // if node is right after prev
   else if (prev->begin + prev->size == node->begin) {
      // merge them together
      prev->size += node->size;
      // if they are connected together
      if (prev->begin + prev->size == rush->begin) {
         prev->size += rush->size;
         prev->next = rush->next;
         if (rush->next != NULL)
            rush->next->prev = prev;
      }
   }
   // else, connect them
   else {
      node->next = rush;
      node->prev = prev;
      rush->prev = node;
      prev->next = node;
   }

}
free_()這個function是要free掉一個已經使用的node。因此為了示範,我們必須從occupied的list中拿出一個node出來free掉。

因此這邊我們就寫了一個function:move_out(),它將會從occupied list中移出第n個node出來。

我們declare一個pointer rush讓它從head_occupy開始去找第n個node,然後用while迴圈去判斷是否到達第n個node:
   MemInfo *rush = head_occupy;
   // get the nth node
   while (n != 0) {
      rush = rush->next;
      n--;
   }
因此這邊的rush就會是第n個node,當然最好的判斷式應當是:
while(rush != NULL &&n != 0) {
   rush = rush->next;
   n--;
}
if (n != 0) {
   printf("occupied list doesn't has %d elements!\n", n);
   return;
}
否則如果這個occupied list根本就不足n個element的時候,rush將不會是well defined的指標,它將會跑到NULL去,接著rush->next將會不存在。

接著稍微注意尾端與前端之後,就該把rush這個node從中移除掉:
   if (rush->next != NULL)
      rush->next->prev = rush->prev;
   if (rush == head_occupy)
      head_occupy = rush->next;
   else
      rush->prev->next = rush->next;
最後return rush便完成了我們要的move_out的function,也就是初步的free就應當先把node移出去。
接著這個移出去的node將會放到free的list上。但是要放在哪裡就必須取決於它的位置begin。因此在free_()中我們需要一個迴圈,為了找出適當的位置:
   while (rush->begin < node->begin) {
      prev = rush;
      rush = rush->next;
   }
如果有找到適當的位置,那麼rush將會是下一個節點,同時prev將會是上一個節點。

接著我們開始判斷位置對應關係,首先如果這個node的begin於free的list中是最小的,那麼它該置放在head的位置:
   if (rush == head_free) {
      // if rush is right after node
      if (rush->begin == node->begin + node->size) {
         // then merge them together
         rush->begin = node->begin;
         rush->size += node->size;
      }
      // otherwise
      else {
         node->next = rush;
         node->prev = NULL;
         rush->prev = node;
         head_free = node;
      }
   }
這時候我們必須要注意的一點就是,如果rush正好記憶體的位置是接在node的後端的話,那麼我們就直接將這兩個node整併成同一個node:
      if (rush->begin == node->begin + node->size) {
         // then merge them together
         rush->begin = node->begin;
         rush->size += node->size;
      }
而如果中間有斷層的話,就不用這樣處理,就直接linked list接上去就可以了:
      else {
         node->next = rush;
         node->prev = NULL;
         rush->prev = node;
         head_free = node;
      }
要注意的是head的前端是NULL,而這邊由於NODE是最小的位置因此他將會是head。

接下來:
   else if (prev->begin + prev->size == node->begin) {
      // merge them together
      prev->size += node->size;
      // if they are connected together
      if (prev->begin + prev->size == rush->begin) {
         prev->size += rush->size;
         prev->next = rush->next;
         if (rush->next != NULL)
            rush->next->prev = prev;
      }
   }
如果他並非擺在head的位置,但是它正巧在記憶體中的位置是在prev的後方接密緊鄰,那麼它應當要跟prev併為同一個node:
      prev->size += node->size;
同時要注意的是,如果這時後面那一個node如果正好也緊密接鄰的話,同樣我們把後面的node與前方已併完的node再次併一次:
      if (prev->begin + prev->size == rush->begin) {
         prev->size += rush->size;
         prev->next = rush->next;
         if (rush->next != NULL)
            rush->next->prev = prev;
      }
這時後要注意的是prev的next指向的地方需要改正。那麼為什麼上一次處裡不需要改正prev->next = node->next呢? 因為node並沒有next,而且prev的next並不需要改。最後的if就是判斷是否為尾端,如果不是尾端我們再行處理,否則NULL的prev是沒有定義的。

最後,如果並沒有併攏情形,那麼就正常處理,前面與node結合,後面與node結合:
   else {
      node->next = rush;
      node->prev = prev;
      rush->prev = node;
      prev->next = node;
   }

2013年9月4日 星期三

link list-小專題-記憶體管理-增加occupy list

// add node to occupy list
void add_to_occupy_list(MemInfo *node)
{
   // set this node to head of the occupy list
   node->prev = NULL;
   if (head_occupy != NULL)
      head_occupy->prev = node;
   node->next = head_occupy;
   head_occupy = node;

}
我們除了要記錄可用的list以外,也同時必須記錄已經使用的list才有意義。因此不管是從first fit又或是best fit return出來的node是已經使用中了,那麼就必須要把這個node加到head_occupy上。這邊的function就是在做這件事情。

而為了節省速度,使用了有點像是先進後出的概念去做這一條head_occupy的list,也就是head_occupy always是指向最新使用的node,當然要節省速度也不是只有這種方法,我們也可以使用tail_occupy去指向這個list的tail,而由於是雙向的list,因此可以使用prev指標便能輕易的移動。

我們用了先進後出,所以這個node將會是head_occupy,因此它的prev將會是NULL,於是便先指定:
   node->prev = NULL;
接著把目前的head_occupy往前接給現在的node,但同時必須先注意head_occupy是否是空指標,因為如果是空指標是沒有定義記憶體空間的,因此它的prev便沒有合法定義:
   if (head_occupy != NULL)
      head_occupy->prev = node;
然後把現在的node往後接head,邏輯同前面的敘述,但我們就直接接head_occupy便可,因為即使head_occupy是空指標,目前的node往後接空指標是我們要的狀況:
  node->next = head_occupy;
最後重新指定head_occupy就大功告成了。

2013年9月3日 星期二

link list-小專題-記憶體管理-best fit allocation

// allocate a memory in a linked list by best fit
MemInfo *best_fit_mem_alloc(unsigned size)
{
   MemInfo *best_fit = NULL, *data, *rush;
   // record the smallest difference
   int smallest_diff = INT_MAX;
   // assigned this from head node for un-used memory
   rush = head_free;
   // find out the best fit
   while (rush != NULL) {
      if (rush->size > size &&
                (rush->size - size) < smallest_diff) {
         smallest_diff = rush->size - size;
         best_fit = rush;
      }
      rush = rush->next;
   }
   // if we could find out the best fit
   if (best_fit != NULL) {
      // allocate a memory
      data = (MemInfo*) malloc(sizeof(MemInfo));
      data->begin = best_fit->begin;
      data->size = size;
      if (best_fit->size > size) {
         best_fit->begin += size;
         best_fit->size -= size;
      }
      else {
         // jusdge if this is head
         if (best_fit == head_free)
            head_free = best_fit->next;
         else
            best_fit->prev->next = best_fit->next;
         // if this is not tail
         if (best_fit->next != NULL)
            best_fit->next->prev = best_fit->prev;
         free(best_fit);
      }
   }
   return (data);
}
基本上best fit和first fit整體的程式邏輯是相同的,唯一不同的地方就是前面要尋找出最適合大小的size的node而已。因此這邊就導入了一個pointer: rush專門用來當成是迴圈從head跑到最後。唯有這種方式才能找到最適合大小的size。

因此我們修改下while的條件為rush != NULL表示rush要從頭跑到尾,然後best_fit的指標就是專門記載"目前最適合的node"的位置,我們並添加了一個smallest_diff就是專門記載"目前最適合的node"的size和我們需要大小的差異。差異越小的越是滿足最適合大小的要件:
   while (rush != NULL) {
      if (rush->size > size &&
                (rush->size - size) < smallest_diff) {
         smallest_diff = best_fit->size - size;
         best_fit = rush;
      }
      rush = rush->next;
   }
所以我們這邊就多添加了一個if就是專門記載best_fit以及smallest_diff,記錄最適合大小的pointer以及size差異值。如果目前的node: rush的size比我們要allocate的size還大,並且size的差異呢,還比上一筆best_fit的差異還小的話,那麼它就會比上一筆best_fit還更適合當最適合大小的node。這邊的邏輯便然是如此。

接下來的程式就會和first fit是一模一樣的做法,詳情就請參考上一篇的first fit allocation。