C专家编程  精编之一     第一章~第三章

C的复杂之处 在于它的指针 ,但是比其指针更为复杂的是它的声明 !!!

你能看懂它们的意思 吗?

apple=sizeof(int)*p  ;   apple=sizeof * p;

j= (char (*)[20])malloc(20);

int   const * grape; 与   int * const grape; 的区别

typedef void (*ptr_to_func)(int);

void (*signal(int sig,void (*func)(int )))(int );  

几个样例:
        一:char *a; const char *b;   a=b;//出现警告.         why?
        二: const int two =2;
            switch(i)
             {
              case 1:printf("case 1 !  /n");
              case two :printf("case 2/n");
              }
            编译出错,说明了.....?
       三:switch(){..}中把 default改成 defau1t (无心之过,或其它标签如defavlt,dafault..)都编译通过 .  why?
       四: apple=sizeof(int)*p  ;   apple=sizeof * p;    //是什么意思?   另外, y=sizeof x; 能编译通过吗?
       五:  j= (char (*)[20])malloc(20);                     //怎么样?
       六:  result=*x/*y ;     //出错?why   ?   
             z=y+++x;   即为: z=y++ +x;    但z=y+ + +x;    z=y+ ++x;   呢?         
      七:  const   int * grape; 
             int   const * grape;
             int * const grape;       //有什么区别?
八:  int (* fun())()        
             int  (* fun())[]        
             int (* funp[])()        //各是什么意思?
九:  void (*signal(int sig,void (*func)(int )))(int ); //什么意思??
       十:  typedef void (*ptr_to_func)(int);                //这个用法,你会吗?
       十一:struct foo{int foo;}foo;  // 合法! 那么,sizeof(foo)又表示哪一个foo呢?
       .........
  以上几个例子是这篇文章的主要内容,是我根据 C专家编程 前三章 的主要内容整理的.
  如果对上面的问题很清楚,那么就没有必要看下去,
  如果对C一点都不了解,也没有必要看下去.
  如果对C非常感兴趣,同样也没有必要看下去,直接看书吧...
问题一:实参char * s 与形参 const char *p是相容的,但是为什么  实参 char * *argv 与形参 const char **p实际上不能相容?
        例:
            foo(const char **p){ }
            main(int argc,char **argv)
            {
               foo(argv);
            }
  //产生warning:argument is incompatible with prototype 即  警告:参数与原型不匹配
     问题产生: 实参 char *s 与 形参const char *p应该是相容的,那么为什么  实参 char * *argv 与形参 const char **p实际上不能相容?
       答案途径:  ANSI C 标准.
     6.3.2.2: 每个实参都应该具有自己的类型.这样它的值就可以赋值给与它所对应的形参类型的对象(该对象的类型不能含有限定符).      也就是说参数传递过程类似于赋值.
     //即:const char **类型的对象可以赋值给一个类型为 char **的值,否则..... //why?
      赋值规则: 6.3.16.1:
     要使上面的赋值合法,必须满足下列条件:
     两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符. 
   //即指向的类型相容且左有右全部限定符. 
  例:char *a;
      const char *b;
      若b=a;       //可行;
      若a=b;       //不可行.警告.
    6.1.2.5说明:char float * 类型并不是一个有限定符的类型------它的类型是"指向一个具有const限定符的char类型的指针",也就是说const限定符是修饰指针所指向的类型,而不是指针本身.  //参见:问题const
    因此,由于char **和const char **都是没有限定行的指针类型,但它们所指向的类型不一样(前者指向char * ,后者指向 const char *).故,它们是不相容的.         
    下面更亲切的解释:
             左操作数:   p-------->Foo (char *)------------>const char ;
             右操作数:   argv---->Baz (char *)------------>char ;
         若:  p=argv;
             则:Foo和Baz所指向的类型是相容的,而且它们本身都没有限定符,所以符合标准的约束条件,两之间赋值合法.
      但是,Foo和Baz并不相容.故  p=argv不合法. //p19~p20页,再看几遍!!!
问题2:关于char ,int ,long int ,float ,double,long double等计算时的转换.
注意:unsigned类型!
尽量不要使用unsigned类型,以免造成边界问题!!!  -1<1000    返回 0
问题3:
  #define TOTAL_ELEMENTS   (sizeof(array)/sizeof(array[0]))   
而不是:
#define TOTAL_ELEMENTs   (sizeof(array)/sizeof(array(int))
原因:第一个可移植性强.
问题4:NUL  与 NULL
NUL:用于结束一个ASC字符串;
NULL:用于指向空指针.
问题5:关于switch
         switch的一个问题是它内部的任何语句都可以加上标签,并在执行时跳转到那里.
         并且所有的标签都是可选的,如:若把default打成  defau1t 它能顺利通过编译,不显示错误.       
         另外,在C中,const并不真正表示常量.如:
          const int two =2;
          switch(i)
           {
              case 1:printf("case 1 !  /n");
              case two:printf("case 2/n");
           }
            编译出错,这并不是switch的过错,但是它却展示 了const其实并不是真正的常量.  //记住,const 是值不可变的变量
问题6:C中相邻字符串编译时自动合并.
                如:printf("I and"
                               "you");
                     相当于  printf("I andyou");及printf("I and""you");
问题7:太多的缺省可见性:
          定义C函数时,在缺省情况下,函数的名字是全局可见的,可以在前面加上冗余的extern,也可不加,效果一样.
          function apple(){/*在任何地方均可见*/}
          extern  function apple(){/*在任何地方均可见*/}
          static   function apple(){/*在这个文件之外不可见*/}
          所以:尽量用static  !!!
    注:关于interpositioning ,还要学习很多东西!
问题8:C中的误做之过.          太多的符号重载!!!!
        static   在函数内部 ,表示变量的值在各个调用之间,一一直保持延续性. 
                   在函数这一级,表示该函数只对本文件可见.
       extern  函数,表示全局(冗余)
                  变量,表示在其他的地方定义.
       void    函数返回类型,
                 指针声明中,表示通用指针的类型.
                 位于参数列表中,表示没有参数.
        *      乘,
                 指针:间接引用 
                声明中,表指针.
         &   AND ,取址
**
          ......
        ()     函数定义中,包围参数表
                调用函数
               改变运算次序
               强制转换.
               定义带参数的宏.
               包围sizeof操作符的操作数.(注:如果它是 类型名 !!!!) 
         一个例子:
                  p=N*sizeof*q;
                  r=malloc(p);   //明白了吧!!
           sizeof的操作数是一个类型名时,必须要有括号,但是如果是变量,则不必加.
                   apple=sizeof(int)*p  ;
                  //什么意思?   由优先级,结合性决定
                  //另y=sizeof x; y=sizeof(x);y=sizeof (int)都可以,但是y=sizeof   int;不行;
  优先级及结合性
::::::::::::::::::::::::::::::::::::::::::::::::::
    1   ()  [] .  ->                                               自左至右
    2    !  ~ ++ -- - (表示负号), (类型),  * (指针) ,&,sizeof      自右至左!!!
    3 ,4  数学运算符  先*/%后+,-
    5   移位
    6   关系  大小(等)
    7   关系  == !=
    8,9,10   位运算    与,异或,或                  &,^,|
    11,12    逻辑运算  &&  ||
    13  条件        ? :                                            自右至左!!!
   14  赋值                                                        自右至左!!!
   15  逗号
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
问题9: 有些运算符的优先级是错误的!
    更正:
    .高于 *  :  *p.f 表示  *(p.f) //即对p取f取偏移,作为指针,然后进行解除引用操作
                                         //解决方案  p->f
    []高于*  :   int *ap[]        //表示指针数组int * (fp[]),不是指向数组的指针.                                          //int (*fp)[]
    ()高于 *  :  int*fp()         //函数返回值是个指针. 
    //注:(),[], .  ,->的优先级最高!!!
    ==和!=高于位操作,也高于赋值.   
    逗号最低       i=1,2  相当于  (i=1),2
问题10:什么是结合性?
            结合性是仲裁者,在几个操作符具有相同的优先级时,决定先执行哪一个.....
问题11:早期gets()中的Bug导致了internet蠕虫.       
           gets()并不检查缓冲区的空间!!!!!
           利用多写的东西,覆盖缓冲区之外的空间,甚至高手可改变函数的返回地址!!!!
           缓冲区溢出攻击??
           解决方案:
           fgets()   //已经限定了长度.
问题12:C中的空格问题!!!!
         最大解析方案:
         z=y+++x;   即为:    z=y++ +x;        但?  z=y+ ++x;   呢?
         z=y+++++x;  呢?    z=y++ ++ +x;还是 z=y++ + ++x;    ?  都错!!!
         result=*x/*y ;     //出错?why   ?    被当作注释!!!
       
        a//*
         //*/b;
        在C中表示a/b,在C++中表示a
问题13:lint程序!!!非常有用!!
            垃圾回收!非常重要!最好在一个代码块中!         
            注意:函数返回值不要为一个指向局部变量的地址,或者动态分配又释放的地址!
问题14:      分析C语言的声明:
     C语言哲学:要求对象的声明形式与它的使用形式尽可能的相仿.
     一个int类型的指针数组被声明为int*p[3]; 并以 *p[i]这样的表达式引用或者使用指针所指向的int数据.所以,它的声明形式和使用形式非常相似.
     这样的好处是:各种不同操作符的优先级在"声明"和"使用"时是一样的.
     缺点很明显, 优先级(15级或者更多)是C中另一个设计不当,过于复杂之处.   
     声明形式和使用形式相似好像是C的独创,但是把两个没有关系,截然不同的东西做成一个样子又有什么重要意义?
     时至今日,在C++中采纳的是:
     int &p ;                 //表示参数的会址调用.     
     C的声明存在的最大问题是你无法以 一种人们所习惯的自然方式从左向右阅读一个声明.在ANSI C 引入 volatile 和 const
     关键字之后,情况更糟糕了,它们只能在声明中,而不能在使用中,从而声明形式和使用形式能完全对上号的越来越少了.   
     而那些从风格上看像是声明,但却没有标识符的东西(如形式参数声明和强制类型转换)看上去显得滑稽.
     如果想要把什么东西的类型强制转换为指向数组的指针,就不得不使用下面的语句来表示这个强制类型转换:       
      char (*j)[20]  ;   /*j表示一个指向数组的指针,数组内有20个char元素*/
      j= (char (*)[20])malloc(20);                     //怎么样? 够晕了吧!!!
      如果把星号两边看上去明显多余的括号拿掉,代码会变成非法的.
问题15:关于 涉及指针和const的声明
      注:const常变量类型,我理解为一次赋值(包括函数参数传递[或曰传值]),终身不变.
        然,const 是作用于它指向的对象上.
     几种不同的顺序:
      const   int * grape;         
      int   const * grape;      //前两种,指针所指的对象是只读的.     
                                //const作用于星号上,即所指的对象上.
      int * const grape;        //指针变量是只读的,const作用于变量上.
      要使对象和指针都是只读的,下面两种方法都能做到这一点:
         const int *const grape;
         int const * const grap;         
//技巧:const优先作用于左(右可能也可以)边的int, long等,如果没有,则作用于左边星号!!!     //const  先类型,后左星        !
问题16:既然连"指向数组的指针",这样概念清晰的语法,它的声明形式也是如此晦涩(问题14),那么对于更复杂的语法形式又将如何.例如下面的声明(取自telnet远程登录程序):
      char * const *(next)();     //这就是C!!!!  明白  ??  后续
问题17:声明是如何形成的?
       首先看看一些C的术语以及一些能组合成一个声明的单独语法成份:声明器!!
       声明器是所有声明的核心!!! 
     简单地说,声明器就是标识符以及它组合在一起的任何指针,函数括号,数组下标等.如下:
    -------------------------------------------------------------------------
          数量                     C中名字                    出现的形式
    ---------------------------------------------------------------------------
         0个或多个             指针                          下列之一:
                                                              *const
                                                              *volatile
                                                              *
                                                              *const
                                                              *volatile const  
   -----------------------------------------------------------------------------
      有且仅有一个        直接声明                   标识符
                                                            或:  标识符[下标]
                                                            或:  标识符(参数)
                                                            或:  (声明器)   
  -----------------------------------------------------------------------------
     一个声明器由上表任意组合组成!但是合法的声明中存在限制条件,以下错误:
     函数的返回值不能是一个函数:  foo()()非法
                           数组:  foo()[]非法
     数组里面不能有函数        :  foo[]()非法
     对这些形式还很迷糊??                                   
      下面则合法:
        int (* fun())()          :函数的返回值允许是一个函数指针. 
        int  (* fun())[]         :指向数组的指针.
        int (* funp[])()         :数组里允许有函数指针.
        int  foo[][]               :数组时允许有其它数组.
//理解:  int (*p)() 为函数指针变量,故  int (*fun[])()为函数指针数组.
//对 int (*fun())[]等,从优先级出发,理解更容易,如(*fun())结合之后,其类似 int foo[] ,它是一个数组,当然这个foo是由* fun()返回的:即函数的返回值是一个指向数组的指针.
问题18:关于struct , union,enum.
     struct:  结构可嵌套,也可有指向自己类型的指针.         
     同时,struct中也可有位段(我觉得易于对位操作):
  //位段必int ,signed int,unsigned int  三者之一. 
                   unsigned int a:1;
                   unsigned int   : 2;   //填充,
   在用struct时,最好把 变量类型的声明 与 变量 的声明分开!
   union:实际上很少使用,因为它的优点其实并不怎么出色,实际上struct出现的几率是union的一百倍.
问题19:优先级规 :理解一个声明,必须 要懂得其中的优先级规则.
      下面是语言律师的定义:       
  A  声明从它的名字开始读取,然后按照优先级顺序依次读取.
  B  优先级从高到低依次是:
        B.1   声明中被括号括起来的那部分.
        B.2   后缀操作符.后缀()表示一个函数,[]表示一个数组.
        B.3   前缀操作符: * 表示指向.....的指针
  C  如果  const  和(或)volatile 的后面紧跟类型说明符(int ,long  ..),那么,它作用于
       类型说明符, 但是一般情况下,它们作用于左边紧邻的指针星号.
          例,再说:   char * const *(*next)();
           A,B.1,B,B2,B3,C
           next  A,next是一个指针B.1,它指向一个函数B.2,该函数返回一个指针B.3,它指向一个 指向字符的常量指针.
           但是下面更易懂: 
 ------------------------------------------------------------------
    步骤                                                        怎么阅读
---------------------------------------------------------------------------------
   1. 取最左边的标识符                               表示"标识符是"
____________________________________________________________
   2.如果右边下一个符号是一个方括号           对于每一对,表示"...的数组"       
__________________________________________________________
   3.如果是一个左括号表示                          至右括号为止的内容" 返回...的函数"
___________________________________________________________________
   4.如果左边的符号是一个左括号                这个左括号把已经处理的部分组合在一
                                                                 起直到遇见对应右括号,回到第2步!!!
___________________________________________________________________
  5.如果左边的符号是下列三个之一:           继续向左,直到不是左边那三个之一:
   const                                                    const:"只读"
   volatile                                                  "volatile "                 
   *                                                          * :"指向...的指针"
                                                               然后重复第四步!!
________________________________________________________________
6.剩下的符号形成基本类型                剩下的可一起阅读,如: static unsigned
-------------------------------------------------------------------
          注:C语言中声明读起来并没有固定方向,一会从左到右,一会又从右读到左.
              开始,我们从左到右,直到找到第一个标识符. 
              然后,当某个符号与上表中匹配时,便把它从声明中处理掉,以后不再考虑.
              在具体的每一步上,我们首先查看右边的符号,然后看左边.
              当所有处理完时,大功告成.
         //技巧:先找标识符,然后 每一步 先右后左,最后处理基本类型.

//注意上表中两个跳转
     例: 再说  char * const *(*next)() ;
          步骤:   1,            "next是..."
                 (2,3,4),       都不匹配
                  5,            *,表示   "指向...的指针"
                  4,          "("和")"匹配, 转到 2. 
                               //"("和")"把已经处理部分组合起来,转到2
                  2,            不匹配
                  3,          表示"返回...的函数"
                  4,           不匹配
                  5,                   表示"指向..的指针"
                  5,                   表示"只读"
                  5,                   表示"指向...的指针"
                  6.                   表示 "char "   
//读完后,把已读的扔掉...
  拼在一起: next是一个指向函数的指针,该函数返回另一个指针,该指针指针指向一个只读的char的指针.            
下面是一个更复杂的例子: char *(*c[10])(int **p) ;
            c是一个指针数组,它的元素类型是函数指针,其所指向的函数返回值是一个指向char的指针.其中,int **p为参数.
问题20   关于  typedef    提示用法: [typedef   基类型   复杂形式];
     如:typedef int Count;  typedef int   Array[10] ;
        typedef void (*func)(int);     
      //这个应该明白吧? 
      // 可以这样用: func  f1,f2;
      // 与  声明 void (*f1)(int); void (*f2)(int);  效果一样.
   它是一种有用并且有趣的声明形式:它为一种类型引入新的名字,而不是为变量分配空间.
   一般情况下,typedef 用于简洁地表示指向其他东西的指针.例如,中断函数  signal(),它用于处理中断,其声明为:
     void (*signal(int sig,void (*func)(int )))(int );
    用上面的技巧分析,可看出它的意思为:   void (*signal(    ))(int );
     signal是一个函数(具有一些令人胆战惊心的参数),它返回一个函数指针,后者所指向的函数接受一个int参数并返回void .
其中一个恐怖的参数是其本身:
  void (*func)(int );它是一函数指针,指向的函数参数为int ,该函数返回类型为void 型.
   下面用typedef来对其进行简化:
    typedef void (*ptr_to_func)(int);                           
     //此时相当于:  ptr_to_func   为   void (*)(int) 
     // 它表示ptr_to_func是一个函数指针,该函数接受一个int参数,返回值为void.
     ptr_to_func signal(int,ptr_to_func);
  //它表示signal是一个函数,它接收两个参数,一个是int,另一个是ptr_to_func
问题21   typedef的缺点:     混乱!!!!  
       它具有与其它声明一样混乱的语法,可以把声明器塞到一个声明中! //缺点之一
       typedef 声明不必放在开始位置中!            //缺点之二
       可在一个typedef 放入几个声明器,如下:    //缺点之三
        typedef int *ptr,(fun)(),arry[5] ;
        /*    ptr是"指向int的指针"类型,
         *    fun是"返回值为int的函数的指针"类型,
         *    arry是"长度为5的int 型数组"类型
         */ 
        千万不要把typedef嵌入到声明的中间部分://缺点之四
       unsigned const long typedef int volatile * kumquat;  //??这是什么意思?
        注: typedef 仅仅是类型创建别名,而不是创建新的数据类型,可以对任何类型进行 typedef声明.   
typedef int (*array_ptr)[100];    //注:为数组指针类型(与指针数组区分开).       
问题22  typede int x[10]与#define x int[10]之间的区别:
             #define 完全是替换.
             typedef 是为已有类型产生一个别名,代表一个完整的类型.
            //注:现在好像已经没有必要再去用#define ,可以直接使用 const.
问题23  typedef的用武之地究竟应该在哪里?
             C中存在多种名字空间:
                 ::标签名(label name).
                 ::标签(tag):用于所有的结构,联合,枚举.
                 ::成员名:每个结构或者联合都有自己的名字空间.
                 ::其它.
        在同一个名字空间里,任何名字必须有唯一性,但是不同空间中,则可相同.
           //产生混乱!!!!
         一:     struct foo{int foo;}foo;                   合法!!!
                  那么,sizeof(foo)又表示哪一个foo呢?             
                //应该是第三个吧.   第一个可以这样:sizeof(struct foo);
                  第二个这样:sizeof(foo); 
                //注:  typedef struct foo1{int foo2;}foo3;
                         foo1:表示结构标签;
                         foo3:结构类型;
                         foo2:成员名;                 
        二:这个更古怪 :
                typedef struct baz{ int baz;}baz;
                            struct baz       var1;
                            baz              var2;
               太多baz了.
           提示:不要为了方便对结构使用 typedef ,省去struct 会减少信息的质量.
      typedef 应该用在:
           数组.结构.指针以及函数的组合类型中.
           可移植类型. typedef short Short; typedef int Long;
           为强制类型转换提供一个简单的名字,如:
                             typedef int (*ptr_to_int_fun)(void);
                                 char *p;
                                 ............
                                 =(ptr_to_int_fun)p;
         始终记住,在结构的定义中使用结构标签,即使并非必须.    目标:清晰,易懂.
问题24:  理解所有分析过程的代码段
       编写一个能够分析C语言声明并把它们翻译成通俗语言的程序.
       简化: 假定输入合法,假定函数的括号内没有参数列表.
       思路:利用堆栈,从左向右读取,把各个标记依次压入堆栈,直到讲到标识符为止.
           继续向右读入一个标记,接着观察标识符左边那个标记.  P72页.
未完待续

C专家编程 精编之一相关推荐

  1. JavaScript 编程精解 中文第三版 零、前言

    零.前言 原文:Introduction 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了<JavaScript 编程精解(第 2 版)> We think ...

  2. 城镇开发边界划定指南_URP精编 | 基于 “双评价”的城镇开发边界划定实证研究...

    致读者 URP今日为您精编的是罗伟玲.吴欣昕.刘小平.张大川.刘鹏华与何家律的<基于 "双评价"的城镇开发边界划定实证研究>,感谢罗伟玲老师提供的精编版.若您想了解更多 ...

  3. c语言程序设计填空带答案,c语言程序设计填空题及答案复习用精编-20210414010859.docx-原创力文档...

    Lele was written in 2021 Lele was written in 2021 C语言程序设计填空题及答案复习用精编 导读:在程序填空题中,已经给出了程序的主干,读者首先要理解程序 ...

  4. Oracle 精编实用手册

    <Oracle精编实用手册> by Else 序章 第1章 Oracle 基础概念 1.1 数据库简介 1.2 Oracle 简介 第2章 Oracle 建立和介绍 2.1 Oracle ...

  5. 《中国人工智能系列白皮书——智能驾驶》精编

    [转] https://www.leiphone.com/news/201710/x7tHyZS8lsohsatP.html 10月12日,中国人工智能学会发布中国人工智能系列白皮书,雷锋网作为邀请媒 ...

  6. 前端网站资源精编!!

    前端网站资源精编!! 目录: 1-------- 走进前端 2-------- jQuery 3-------- CSS 4-------- Angularjs 5-------- ES6 6---- ...

  7. 《Android开发源码精编解析》最新PDF版开源,安卓工程师进阶实战

    对于程序员而言,提高源码的阅读与理解能力是提升自我技术水平和编程功力的捷径. 阅读优秀的Android源码.熟悉源码框架对我们日常的开发工作有诸多好处: 有助于我们学习其中的设计模式.思想.架构: 有 ...

  8. Golang精编100题-搞定golang面试

    Golang精编100题 能力模型 级别 模型 初级 primary 熟悉基本语法,能够看懂代码的意图: 在他人指导下能够完成用户故事的开发,编写的代码符合CleanCode规范: 中级 interm ...

  9. 计算机网络管理课程内容与考核要求,计算机网络管理课程教学探索_精编版

    <计算机网络管理课程教学探索_精编版>由会员分享,可在线阅读,更多相关<计算机网络管理课程教学探索_精编版(6页珍藏版)>请在人人文库网上搜索. 1.精编范文 计算机网络管理课 ...

最新文章

  1. Microbiome:16S扩增子测序研究中定量变异和生物量影响
  2. Python基础-第二天
  3. robotframework ie浏览器中click button不可用_RobotFramework自动化Selenium2Library库常用关键字...
  4. 5加载stm32 keil_快速入门STM32单片机-软件篇
  5. 人类为啥比小鼠发育更慢?同日两篇《科学》找到意想不到的原因
  6. Cmake参数详细介绍
  7. jedis高版本的JedisPoolConfig没有maxActive和maxWait
  8. 给csdn主页添加个人联系方式或二维码
  9. linux文件IO的操作
  10. Qt显示调用C++的dll
  11. SQLCel匹配原数据信息,更新原数据所有信息并插入新数据的过程
  12. xcodebuild命令行打包发布ipa
  13. Windows10上安装VS2017社区版操作步骤
  14. vue 身份证格式校验
  15. python 数据分析 |3. Pandas 学习
  16. IDempiere介绍
  17. 【目标一】2019年9月15日 - 2019年9月22日晚 --- 周计划
  18. 畜牧公众号下添加一键拨号
  19. 购物表单mysql还是mongodb_日志数据是选择mysql 还是 mongodb 还是 postgredb
  20. 一、Tomcat概述

热门文章

  1. 前端知识点整理(待续)
  2. Django Xadmin 官方文档 之五 Xadmin 插件制作
  3. 不经一番寒彻骨,安得梅花扑鼻香?
  4. 全量备份,差量备份,增量备份的异同
  5. [Music]30首经典萨克斯名曲
  6. 冠状面、矢状面、横断面
  7. Git之一次Push的回滚之旅
  8. 网络工具之HCL模拟器安装及使用 | 寻找C站宝藏
  9. matlab标线,如何在matlab中以方位角画线?
  10. easyopen——一个简单易用的接口开放平台