在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ C/ 0x0E-單線程備份(下)
0x0E-單線程備份(下)
0x11-套接字編程-1
0x05-C語言指針:(Volume-1)
0x13-套接字編程-HTTP服務(wù)器(1)
0x0C-開始行動
C 語言進(jìn)階
第一部分
0x05-C語言指針(Volume-2)
0x08-C語言效率(下)
0x07-C語言效率(上)
0x04 C代碼規(guī)范
0x0F-多線程備份
0x05-C語言變量
第四部分
0x16-套接字編程-HTTP服務(wù)器(4)
0x0D-單線程備份(上)
總結(jié)
0x01-C語言序言
0x15-套接字編程-HTTP服務(wù)器(3)
0x14-套接字編程-HTTP服務(wù)器(2)
0x17-套接字編程-HTTP服務(wù)器(5)
第三部分
我的C語言
0x06-C語言預(yù)處理器
0x09-未曾領(lǐng)略的新風(fēng)景
0x0A-C線程和Glib的視角
第二部分
0x10-網(wǎng)絡(luò)的世界
0x12-套接字編程-2
0x03-C代碼
0x0B-C語言錯(cuò)誤處理

0x0E-單線程備份(下)

0x0E-單線程備份(下)

寫在最前方

  • 按部就班的完成一件事情,是十分美妙的感覺。
  • 在這里并沒有使用Makefile系列的構(gòu)造工具,而是使用集成開發(fā)環(huán)境直接一站式的完成所有的工作,而我們只需要專注于編寫有用的代碼即可。
  • 但是對于這些構(gòu)造工具的功能還是需要了解的,到了性能瓶頸期,往往是需要這些東西的微調(diào)來進(jìn)行提升,就像算法為什么有那么多的排序算法,看上去復(fù)雜度都是一樣的,但是快速排序卻往往比堆排序要快?不就是因?yàn)榫植啃钥焖倥判蛞獌?yōu)于堆排序嗎?換句話說就是緩存的命中率高
  • 不了解底層,永遠(yuǎn)也無法理解這個(gè)解釋,但是前方已經(jīng)有提到過什么叫做空間局部性時(shí)間局部性,至少能有些理解了。
  • 構(gòu)造工具也是如此,例如,編譯了源文件生成了庫文件,當(dāng)我們在某個(gè)函數(shù)中通過該庫調(diào)用這個(gè)庫中的某些函數(shù),這個(gè)庫會在在一開始就加載進(jìn)我們的程序。當(dāng)我們的程序十分龐大的時(shí)候,也許我們希望在使用的時(shí)候才使用它,那么就需要延遲加載這個(gè)編譯器技術(shù)。如果沒有了解過構(gòu)造工具,這些根本不會懂,并且某些情況下Unix, Linux, Windows對于庫的加載方式是不同的,這些都是需要了解的,但是我們現(xiàn)在的確沒有必要,這個(gè)程序滿打滿算也就是四五百行的代碼,不太需要考慮這些。

寫在中間

  • 上回完成了界面的大部分功能,剩下的便是備份這個(gè)主要功能。
  • 在完成備份之前,首先想想要如何構(gòu)造這個(gè)備份模型

    • 既然是備份,如果不想擴(kuò)展為多線程的形式,參考第一次寫的遍歷函數(shù)(show_structure)直接找到文件便調(diào)用Windows API(稍后介紹)進(jìn)行復(fù)制即可,不需要講待備份的文件路徑保存下來。
    • 如果要考慮多線程擴(kuò)展,我們就需要從長計(jì)議。
    • 對于一個(gè)備份模型,最好的莫過于使用一個(gè)隊(duì)列,依舊實(shí)行的是遍歷模式,但是將找到的文件路徑保存,并放入一個(gè)先進(jìn)先出隊(duì)列中,這樣我們就能夠保證在擴(kuò)展成多線程的時(shí)候,可以有一個(gè)很清晰的模型參考。
    • 那么現(xiàn)在的任務(wù)就是實(shí)現(xiàn)這個(gè)用于備份的隊(duì)列模型。
  • 隊(duì)列模型

    • 應(yīng)該有一個(gè)容器空間:用于存放路徑
    • 有隊(duì)首隊(duì)尾標(biāo)志
    • O(1)復(fù)雜度的檢查隊(duì)列是否為空的接口或標(biāo)志
    • O(1)復(fù)雜度的返回容器容量的接口或標(biāo)志,容器容量應(yīng)該固定不變
    • 使用一些面向?qū)ο蟮暮谀Х?,保存一些操作函?shù)防止代碼混亂。
      • 初始化函數(shù)
      • 釋放函數(shù)
      • 彈出操作函數(shù)
      • 壓入操作函數(shù)
  • 隊(duì)列實(shí)體

    • 考慮到要存儲的是字符串,并且由于Windows API的參數(shù)需求,對于一個(gè)文件,我們需要存儲的路徑有兩個(gè)<源路徑,目的路徑>,對此應(yīng)該再使用一個(gè)路徑模型結(jié)構(gòu)體包裹他們,則空間的類型就相應(yīng)改變一下
  • 新建 Queue.h Queue.c

    • Queue.h

          typedef struct _vector_queue queue;
          typedef struct _combine combine;
      
                  |    返回值    | | 函數(shù)類型名 ||   參數(shù)類型   |
          typedef int             (*fpPushBack)(queue * __restrict, const char * __restrict, const char * __restrict);
          typedef const combine * (*fpPopFront)(queue *);
          typedef void            (*fpDelete)(queue *);

      五個(gè)typedef不知道有沒有眼前一懵。,希望能夠很好的理解

      前兩個(gè)是結(jié)構(gòu)體的聲明,分別對應(yīng)著 隊(duì)列模型 和 路徑模型。

      后兩個(gè)是函數(shù)指針,作用是放在結(jié)構(gòu)體里,使C語言的結(jié)構(gòu)體也能夠擁有一些簡單的面向?qū)ο蠊δ?,例?strong>成員函數(shù)功能,原理就是可以給這些函數(shù)指針類型的變量賦值。稍后例子更加明顯。試著解讀一下,很簡單的。

          struct _combine{
          char * src_from_path; /* 源路徑 */
          char * dst_to_path;   /* 目的路徑 */
          };
      
          struct _vector_queue{
              combine **      path_contain; /* 存儲路徑的容器主體 */
              unsigned int    rear;         /* 隊(duì)尾坐標(biāo) */
              unsigned int    front;        /* 隊(duì)首坐標(biāo) */
              int             empty;        /* 是否為空 */
              unsigned int    capcity;      /* 容器的容量 */
              fpPushBack      PushBack;  /* 將元素壓入隊(duì)尾 */
              fpPopFront      PopFront;  /* 將隊(duì)首出隊(duì) */
              fpDelete        Delete;    /* 析構(gòu)釋放整個(gè)隊(duì)列空間 */
          };
      
          /**
           * @version  1.0 2015/10/03
           * @author   wushengxin
           * @param    object 外部傳入的對象指針,相當(dāng)于 this
           * @function 初始化隊(duì)列模型,建立隊(duì)列實(shí)體,分配空間,以及設(shè)置屬性。
           */
          int newQueue(queue* object);

      可以看到,上方的函數(shù)指針類型,被用在了結(jié)構(gòu)體內(nèi),此處少了一個(gè)初始化函數(shù),是因?yàn)椴淮蛩惆阉?dāng)作成員函數(shù)(借用面向?qū)ο笮g(shù)語)

      在使用的時(shí)候可以直接obj_name.PushBack(..., ..., ...);

      更詳細(xì)的可以看后面的實(shí)現(xiàn)部分。成為成員函數(shù)的三個(gè)函數(shù),將被實(shí)現(xiàn)為 static 函數(shù),不被外界訪問。

  • queue.c

      int newQueue(queue * object)
      {
          queue*      loc_que = object;
          combine**   loc_arr = NULL;
    
          loc_arr = (combine**)Malloc_s(CAPCITY * sizeof(combine*));
          if (!loc_arr)
              return 1;
    
          loc_que->capcity = CAPCITY; /* 容量 */
          loc_que->front = 0;        /* 隊(duì)首 */
          loc_que->rear = 0;        /* 隊(duì)尾 */
    
          loc_que->path_contain = loc_arr; /* 將分配好的空間,放進(jìn)對象中 */
          loc_que->PushBack = push_back;
          loc_que->PopFront = pop_front;
          loc_que->Delete   = del_queue;
    
          return 0;
      }

    在初始化函數(shù)中,可以看到,設(shè)置了隊(duì)首隊(duì)尾以及容量,分配了容器空間,配置了成員函數(shù)。

    最后三句配置函數(shù)的語句中,push_back, pop_front, del_queue在后方以static 函數(shù)實(shí)現(xiàn)。

    但是由于沒有聲明,所以切記要將三個(gè)static函數(shù)的實(shí)現(xiàn)放在newQueue的前方

      /**
       * @version  1.0 2015/10/03
       * @author   wushengxin 
       * @param    object 外部傳入的對象指針 相當(dāng)于 this
       * @function 釋放整個(gè)隊(duì)列實(shí)體的空間
       */
      static void del_queue(queue * object)
      {
          Free_s(object->path_contain);
          return;
      }
    
      /**
       * @version  1.0 2015/10/03
       * @author   wushengxin
       * @param    object 外部傳入的對象指針 相當(dāng)于 this
                   src    源路徑
                   dst    目的路徑
       * @function 將外部傳入的<源路徑,目的路徑> 存入隊(duì)列中
       */
      static int push_back(queue * __restrict object, const char * __restrict src, const char * __restrict dst)
      {
          int times = 0;
          char*    loc_src = NULL; /* 本地變量,盡量利用寄存器以及緩存 */
          char*    loc_dst = NULL;
          combine* loc_com = NULL;
          queue*   loc_que = object;
    
          size_t   len_src = strlen(src); /* 獲取路徑長度 */
          size_t   len_dst = strlen(dst);
          size_t   rear = loc_que->rear;   /*獲取隊(duì)尾*/
          size_t   front = loc_que->front; /*獲取隊(duì)首*/
    
          loc_src = Malloc_s(len_src + 1); /* 分配空間 */
          if (!loc_src)
              return 1;
    
          loc_dst = Malloc_s(len_dst + 1);
          if (!loc_dst)
              return 2;
          strcpy(loc_src, src);
          strcpy(loc_dst, dst);
    
          loc_com = Malloc_s(sizeof(combine));
          if (!loc_com)
              return 3;
          loc_com->dst_to_path = loc_dst; 
          loc_com->src_from_path = loc_src;
    
          loc_que->path_contain[rear++] = loc_com; /* 將本地路徑加入實(shí)體 */
          loc_que->rear = (rear % CAPCITY);     /* 用數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列的步驟 */
    
          if (loc_que->rear == loc_que->front)  
              loc_que->empty = 0;
          return 0;
      }
    
      /**
       * @version  1.0 2015/10/03
       * @author   wushengxin
       * @param    object 外部傳入的對象指針
       */
      static const combine * pop_front(queue* object)
      {
          size_t   loc_front = object->front;                   /*獲取當(dāng)前隊(duì)首*/
          combine* loc_com   = object->path_contain[loc_front]; /*獲取當(dāng)前文件名*/
          object->path_contain[loc_front] = NULL;     /*出隊(duì)操作*/
          object->front = ((object->front) + 1) % 20; /*完成出隊(duì)*/
    
          if (object->front == object->rear)
              object->empty = 1;
          else
              object->empty = 0;
          return loc_com;
      }

    一個(gè)一個(gè)的說這些函數(shù)

    del_queue:釋放函數(shù),直接調(diào)用Free_s

    push_back:壓入函數(shù),將外部傳入的兩個(gè)原始的沒有組成的路徑字符串,組合成一個(gè)combine,并壓入路徑,每次都判斷并置是否為空標(biāo)志位,實(shí)際上這個(gè)函數(shù)中有累贅代碼的嫌疑,應(yīng)該再分出一個(gè)函數(shù),專門用來分配三個(gè)空間,防止這個(gè)函數(shù)過長(接近40行)

    pop_front:彈出函數(shù),將隊(duì)列的隊(duì)首combine彈出,用于復(fù)制,但是這里有一個(gè)隱患,就是要將釋放的工作交給外者,如果疏忽大意的話,隱患就是內(nèi)存泄漏。

    沒有特地的提供一個(gè)接口,用來判斷是否為空,因?yàn)楫?dāng)編譯器一優(yōu)化,也會將這種接口給優(yōu)化成直接使用成員的形式,某種形式上的內(nèi)聯(lián)。

  • 隊(duì)列模型設(shè)計(jì)完畢,可以開始設(shè)計(jì)備份模型
  • 備份模型可以回想一下之前的遍歷函數(shù),大體的結(jié)構(gòu)一樣,只是此處為了擴(kuò)展成多線程,需要添加一些多線程的調(diào)用函數(shù),以及為了規(guī)格化,需要添加一個(gè)二級界面
  • 先設(shè)計(jì)一下二級界面

  • 二級界面

    • 思考一下,這個(gè)界面要做什么
      • 選擇是否開始備份
      • 并且源路徑需要在此處輸入
      • 返回上一級
    • 新建 backup.h backup.c 文件
      • 在主界面選擇 1 以后就會調(diào)用二級界面的函數(shù)
      • 列出二級界面的選項(xiàng)
        • 1 Start Back up
        • 2 Back To last level
    • backup.h

          /**
           * @version  1.0 2015/10/03
           * @author   wushengxin
           * function  顯示二級界面
           */
          void sec_main_windows();
    • backup.c

          void sec_main_windows()
          {
              char tmpBuf[256];
              int selects;
              do{
                  setjmp(select_jmp);
                  system("cls");
                  puts("-------------------1. Back Up------------------ ");
                  puts(" For This Select, You can choose Two Options: ");
                  puts(" 1. Start Back up (The Directory Path That You Enter LATER) ");
                  puts(" 2. Back To last level ");
                  puts("----------------------------------------------- ");
                  fprintf(stdout, "Enter Your Selection: ");
                  fgets(tmpBuf, 256, stdin);
                  sscanf(tmpBuf, "%d", &selects);
                  if (selects != 1 && selects != 2 )
                  {
                      fprintf(stdout, "\n Your Select \" %s \" is Invalid!\n Try Again \n", tmpBuf);
                      longjmp(select_jmp, 1);
                  }
      
                  switch (selects)
                  {
                      jmp_buf enter_path_jmp; 
                  case 1:
                  {
                      char tmpBuf[LARGEST_PATH], tmpPath[LARGEST_PATH]; /* 使用棧分配空間,因?yàn)橹挥梅峙湟淮?*/
                      setjmp(enter_path_jmp);         /* enter jump to there */
                      puts(" Enter the Full Path You want to BackUp(e.g: C:/Programing/)");
                      fprintf(stdout, " Or Enter q to back to select\nYour Enter : ");
                      fgets(tmpBuf, LARGEST_PATH, stdin);
                      sscanf(tmpBuf, "%s", tmpPath);
                      if (_access(tmpPath, 0) != 0)   /*檢查路徑是否存在,有效*/
                      {
                          if (tmpPath[0] == 'q' || tmpPath[0] == 'Q') 
                              longjmp(select_jmp, 0); /* 回到可以選擇返回的界面 */
                          fprintf(stderr, "The Path You Enter is Not Exit! \n Try Again : ");
                          longjmp(enter_path_jmp, 0); /* enter jump from here */
                      }
                  }
                      break;
                  case 2:
                      return;
                  default:
                      break;
                  }/* switch */
              } while (1);
              return;
          }
      這個(gè)函數(shù)只說幾點(diǎn),首先是`switch`的`case 1`,之所以用**花括號**包裹起來的原因是,這樣才能在里面定義**本地變量**,直接在冒號后面定義是**編譯錯(cuò)誤**,這個(gè)特性可能比較少用,這里提一下,前面也有說過。

寫在最后方

  • 剩下的就是編寫主要的功能函數(shù)和線程調(diào)用函數(shù)了。
上一篇:0x0C-開始行動下一篇:第四部分