C語言基礎筆記

本人學習期間總結的所有C語言筆記:


1.運算符
   一元運算符優先級大於二元運算符
   ,運算符優先級最低
   []大於*號運算符(用於指針數組時)
   ! > 算術運算符 > 關係運算符 > && > || > = > ,

2.字符型
   char沒有明確的定義爲signed或unsigned,如果需要注意,要自己加上
   '\''    '\"'    '\\'    '%%'    '\?'
   警報'\a'        回退'\b'        換頁'\f'        換行'\n'        回車'\r'        水平tab'\t'    垂直tab'\v'

   getchar();從stdin緩衝區中讀取
   getch();從鍵盤緩衝區中讀取,在win下不回顯,在Unix下回顯 在<curses.h>中

   字符測試函數:【包含在<ctype.h>中】
   1) int isalnum(int c)      //測試c是否是字母或數字,是的話返回爲真,否則爲假
   2) int isdigit(int c)      //測試c是否是數字
   3) int isalpha(int c)      //測試c是否是字母
   4) int islower(int c)      //測試c是否是小寫字母
   5) int isupper(int c)      //測試c是否是大寫字母
   6) int tolower(int c)      //轉換爲小寫字母
   7) int toupper(int c)      //轉換爲大寫字母
   8) int iscntrl(int c)      //測試c是否是控制字符
   9) int isspace(int c)      //測試c是否是空格
   10)int isgraph(int c)      //測試c是否是除了空格外的可打印的字符
   11)int isprint(int c)      //測試c是否是可打印字符
   12)int ispunct(int c)      //測試c是否是除了空格,數字,字母外的可打印字符,即是否爲符號

3.字符串
   字符串就是一個字符數組,結尾以'\0'作爲標誌
   一個字符佔用一個字節(漢字佔用兩個字節),從左到右依次存放,在字符串的結尾自動加入一個字節的結束標誌'\0'
   'a'與"a"的不同:'a'代表一個字符佔用一個字節;"a"代表一個字符串佔用兩個字節.
   字符串初始化:char str[10]="abc123!@#";//不能寫成char str[10];str="dfh";(初始化聲明的時候的=不代表賦值操作,只是初始化)
   gets()
   puts()

   字符串轉換函數:
   1)int atoi(const char *str)        //將字符串類型的數據轉換爲整型,參數爲字符串首地址
   2)double atof(const char *str)        //將字符串類型的數據轉換爲雙精度浮點數
   字符串庫函數:
   1) strlen()
   2) strcpy()    strncpy()【複製指定字符個數的字符串】char *strncpy(char *s1, const char *s2, size_t n);
   3) strcmp()    int strncmp(const char *s1, const char *s2, size_t n);【比較前面n個字符】
   4) strcat()    可以用在這種情況下:char ping[8]="ping ",ip[32]="www.baidu.com"; strcat(ping,ip); system(ping);
   5) strtok()    字符串分割函數以s2爲標準分割s1,char *strtok(char *s1, const char *s2);
   6) bzero(void *s, size_t n)    字符串清理,參數1是要清空的字符串,參數2是要清空的位數
   7) memset(void *s, int c, size_t n)    字符串填充,參數2爲填充進去的內容,參數3爲要填充的字節數
   8) strchr(const char *s, int c)        在s中查找c第一次出現的地址
   9) strrchr(const char *s, int c)    在s中查找c最後一次出現的地址
   10)strstr(const char *s1, const char *s2)    在s1中查找s2第一次出現的地址

4.變量(Mac下)
   int    4個字節        -2e15~2e15-1
   float    4個字節        有效位數6~7位
   double    8個字節        有效位數15~16位
   char    1個字節        ASCII碼字符,或-128~127
   定義一個變量時,系統就自動的分配了空間,未賦值前,是隨即放入的一個垃圾值

5.無符號、整形10進制、8進制、16進制
     正常:u   d   o   x
   短: hu  hd  ho  hx
   長: lu  ld  lo  lx

6.浮點數
   默認情況下,浮點數是以雙精度存儲的,要明確使用的是單精度,要在數字後面加上字母f(如:57.0f),如果必須以long double格式存儲,要在數     字的末尾加上字母l(如:57.0l)。    
   當讀取double類型的數值時,在e、f、g前放置字母l,但是l只能在scanf中使用,不能在printf中使用,在printf中e、f、g就能表示double型
   當讀或寫long double類型的值時,在e、f、g前放置字母L;

7.scanf函數和printf函數
   scanf函數在讀入數字時,會自動跳過空白字符,但在讀入字符時,不會跳過空白字符
   讀寫一個單獨的字符時,可以用getshar和putchar代替:[ch=getchar();    putchar(ch)]

8.sizeof(類型名)
   sizeof返回的值是無符號的整數
   可以用sizeof(a)/sizeof(a[0])計算數組長度

9.類型定義(typedef)
   typedef int Bool;    //(後面變量名的首字母最好大寫)
   Bool flag;    //類似與使用了int flag
   C語言庫自身使用typedef定義了一些不同的類型名,這些類型名通常以_t結尾,比如clock_t;

10.宏定義    
   【定義宏】#define BOOL int
       數組和指針不能用宏定義,宏定義中的替換列表爲空也是合法的,也可以包含對另一個宏的調用,但宏不能調用它本身.
       一個數字常量,如果不是0和1,就最好定義成宏.
   【帶參數的宏】 #define  標示符(x1,x1,…,xn)  替換列表    //參數在替換列表中都要放在圓括號中
       #運算符將一個宏的參數轉換爲字符串字面量;##運算符可以將兩個記號(如標示符)粘在一起,成爲一個記號.
       #define CONCAT(x,y)  x##y    //CONCAT(a,b)會得到ab,但CONCAT(a,CONCAT(b,c))不會得到abc,而是aCONCAT(b,c)
   【在宏中放入語句】放在do{ … }while(0)中,這時while後可以不加分號,在調用宏的時候再加分號;如果定義的時候就有分號,在調用的時候就不能再加分號了.
   【取消宏】#undef 標示符

   【預定義宏】_DATE_宏(mm dd yyyy)和_TIME_宏(hh:mm:ss)指明程序編譯的時間,是字符串字面量.
         _LINE_宏: 被編譯的文件的行數,是整型常量    _FILE_宏: 被編譯的文件的名字,是字符串字面量
         _STDC_宏: 如果編譯器接受標準C,值爲1,是整型常量

11.數組
   定義數組時,數組名不能和變量名相同,也不能和系統關鍵字一樣
   數組的地址就是數組元素的首地址,使用數組名a,代表的就是數組a[]的地址
   多維數組:不常用,因爲指針數組更加的靈活好用
   無論是一維數組還是多維數組,通過單詞const作爲數組聲明的開始可以把數組變爲“常量”;
   (如:const int months[]={31,28,31,30,31,30,31,31,30,31,30,31};)
12.函數
   在調用函數前,都需要先聲明函數,函數聲明類似與函數定義的第一行,函數的聲明必須與函數的定義一樣。
   【函數聲明】 返回類型  函數名(形式參數);    【編譯器只預處理函數,但是不分配內存】

   【函數定義】 返回類型  函數名(形式參數)    【開闢內存空間,分配內存給函數,函數入口地址爲函數名所在地址】
          {
           聲明
           語句
          }    

   【函數調用】 函數名(實參);    

   函數無法返回數組;如果忽略返回類型,會假定爲int型;
   如果返回爲空類型,必須寫明void,如果沒有形參,也要寫明爲void.如:void function(void)

   數組做函數參數的時候,傳遞的是數組名,也就是地址
   實參是通過值來傳遞的,在調用函數時,計算出實參的值然後傳遞(賦值)給對應的形參

   函數分類:
   1)從用戶角度分有:
   系統函數:庫函數,不需要用戶定義,包含頭文件就可以直接使用的函數,printf();scanf();system();
   自定義函數:用戶根據自己的需要,聲明函數的返回值類型,函數名,形參列表;但是必須符合函數聲明的規則。可以是有參、無參、有返回值、無返回             值、還可以是函數的重寫。
   2)從返回值的角度:
   有返回值:return,(函數有且只能返回一個返回值),可以是基本數據類型和指針等。
   無返回值:void,在C89標準中可以忽略返回值類型,UNIX中默認爲int,Window VC中爲void;在C99標準中不可以忽略返回值類型。
   3)從參數的角度:
   帶參數:需要告訴編譯器參數的類型和數量
   int main(int argc,char *argv[]);
   int main(int argc,char **argv);
   不帶參數:參數列表爲空,void,可以省略,即int main()或int main(void);就是不需要參數來參與運算。

13.返回語句
   return:【return 表達式】如果return中表達式的類型和函數返回的類型不一致,系統會把表達式的類型轉換成返回類型
   exit:任何函數中都可以使用,【exit(表達式)】;包含在#include <stdlib.h>頭文件中。
   main函數的返回值是狀態碼,0表示正常結束,非0爲異常結束。    

14.遞歸
   遞歸是函數嵌套調用的一種特殊形式,就是當一個函數調用另外一個函數的過程時,另一個函數恰好是它本身。
   遞歸產生的條件:直接或間接的調用它本身,有一個出口(即結束遞歸的條件),要有一個明顯的公理或公式,有規律可循。
   1)直接遞歸調用
   2)間接遞歸調用

   注意:
   1)遞歸調用在發生時,消耗了大量的系統資源,反覆讀寫棧內存,CPU資源,會降低系統性能,不可控或深層遞歸都會造成系統不穩定
   2)我們在開發過程中使用的都是淺層遞歸,如Windows搜索,使用了多線程,淺層遞歸(一般不超過3層)
   3)遞歸調用也被用於分治算法
   4)能不用就不用遞歸,如:.(){ .|. };.  (.本身是一個函數,()爲.函數的參數列表,;前爲.函數的定義,最後一個.是調用這個函數)  

15.迭代
   迭代法又叫做遞推,凡是能用遞歸來實現的迭代或遞推都能實現。
   八皇后問題,水仙花束問題,兔子繁殖問題,約瑟夫出圈問題,快速排序,漢諾塔

16.變量作用域
   全局變量:
   局部變量:

17.指針
   簡單的說,指針就是地址。
   內存中的最小存儲單元是字節,一個字節有8位,每一位都有自己的地址,指針就是他們的首地址。
   基本數據類型的指針,就是他們在內存中的首地址。
   構造類型的指針,數組,他的位置就是,數組名稱所在內存的首地址,即a[0]的地址或者是數組名。
   函數類型的指針,就是函數的入口地址,即函數名所在內存的首地址。

   指針變量:和其他基本數據類型的變量類似,特殊的是他是能保存地址的變量。
   【定義指針變量】類型標示符 *變量名(int *p);  

   指針變量初始化:
       int *p=NULL;
       int *q=&a;                                        
       int *s=100;
   造成野指針的三種方式:
       1.定義指針的時候沒有初始化
       2.指針指向一個局部變量,當局部變量執行完畢釋放後,仍然指向該局部變量的時候(用完置NULL即可)
       3.當指針指向一塊動態的內存空間時malloc(new)使用完畢被free(delete)釋放後,仍然指向該空間,(用完置NULL即可)

18.指針和常量的關係
   1)常量指針:
       指針指向了一個常量的指針變量,常量的內容不可以修改,但是地址可以修改。
       int const *p;    const在*號後,是p的值不能修改;const在*號前,是*p的內容不能修改
   2)指針常量
       是個常量,是指指針變量P的值不能被修改,p是個常量,而指針指向的對象的值是可以改變的。
       int *const p;

19.指針和數組的關係
   1)數組指針
       指向一維數組的指針,即數組的首元素
       int a[10];int *p=NULL;p=a;
       是一個指向二維數組一行的指針變量,即二維數組的首行的地址
       int (*p)[表達式];    
   2)指針數組
       數組元素是指針變量
       int *p[表達式];

20.指針和函數的關係
   1)函數指針
       就是指向函數名的指針變量
       int (*p)(形參列表);    
   2)指針函數
       它是一個返回指針類型的函數,即函數的返回類型是指針
       int *p(形參列表);
       如:int *adr(int x)
          {
           return &x;
          }
       這種情況,返回值還可以繼續作爲函數的參數來使用,如回調函數,比如Unix信號和信號量,signal(),malloc()

21.指向指針的指針
   int a=0;  int *p=&a;  int **q=&p;    //*p==a,  **q==a  

22.結構體(struct)
   能夠保存不同數據類型的一種構造數據類型
   【關鍵字】struct
   【定義結構體】    struct 結構體名{
              成員變量1;
              成員變量2;
              …
              成員變量n;

           };    //分號表示結構體定義完畢
   【結構體變量初始化】(結構體變量就是包含結構體所有屬性的一個變量)
           struct struct_name 變量名={ , , };    //struct struct_name 整體是變量的類型
       或    struct struct_name{
              成員變量1;
              成員變量2;
           }object_name={ , , };
   【引用結構體成員變量】
           .   或  ->(用於指針的時候)

   【結構體和指針】
       1)指向結構體的指針
           struct struct_name *p;    //p是指針變量名
           p=&object_name;        //&取地址符不能忘

   【結構體的嵌套】
       一個結構體中嵌套了另一個結構體
       struct struct_name{
           成員變量1;
           成員變量2;
           struct struct_name_a{
               成員變量a;
               成員變量b;
           }object_name_a;
       }object_name;
       【引用】object_name.object_name_a.b;

   【結構體變量(可以用指針)作爲函數參數使用】
       void struct_function(struct struct_name object_name_temp);
       void struct_function(struct struct_name *p);

   【結構體變量作爲函數的返回值】
       struct struct_name function_name()
       {
           return object_name;
       }

   【結構體數組】
       數組中的每個元素都是一個結構體變量
       struct struct_name{
           成員變量1;
           成員變量2;
       }數組名[表達式];        //表達式可以爲空,爲空的時候是不定長度的數組

23.聯合體(union)
   用戶自定義的數據類型,和結構體不同的是結構體變量獨立使用各自成員的內存空間,聯合體的所有成員共享存儲空間,即使用聯合體省內存.
   定義聯合體和結構體一樣,只需要把struct改成union即可.

24.枚舉(enum)
   枚舉的成員中只能用一種類型,成員內不指定序號的時候都以0開始,C語言把枚舉變量和常量作爲整數來處理
   enum color{red, black, blue}a,b;    //中間以逗號隔開

25.鏈表
   鏈表是線性表的一種,是線性表的鏈式存儲;是一種使用動態內存分配的,能保存不同數據類型的一種數據結構.構成鏈表的最小單位稱爲節點.
   1)單鏈表
     有一個頭結點(非空)鏈表,結點之間有前驅和後繼,尾結點無後繼結點,指向空.節點有兩部分組成數據域和指針域.頭結點用於判斷鏈表是否爲空.
   2)雙鏈表
   3)循環鏈表

26.位操作
   1)位與&
       用於置0
   2)位或|
       用於置1
   3)位異或^
       用於加密和解密:
       0000 1100    0000 0110    (用第一次異或得到的值,再進行異或)
        ^    0000 1010    ^    0000 1010    (用同一個值作爲異或的運算)
       --------------  --------------
       0000 0110    0000 1100    (重新得到了之前的值)

       交換a,b的值:  a=a^b; b=a^b; a=a^b;
   4)位取反~
       都是用補碼來取反的
       連帶符號位一起取反,取反之後符號位爲1的話(補碼),減去1(反碼),除去符號位其它位取反(原碼)
   5)左移<<    
       往左移,低位補0,左移一位相當於乘以2,左移n位相當於乘以2的n次方
   6)右移>>
       往右移,高位補符號位,右移一位相當於除以2,右移n位相當於除以2的n次方
       Mac下左移右移符號位不變
27.內存管理
     系統級內存管理(Unix系統提供的內存管理)
   1)物理內存管理
   2)虛擬內存管理
   3)頁面文件getpage
   4)內存映射mmap
   5)共享內存
   brk();//在分配內存時效率很高,只有3個值:大於0(申請該大小的內存空間),等於0(完全釋放內存),小於0(在當前的內存空間上釋放該大小的空間)
   sbrk();    
   OC中用的內存管理,計數,當計數爲0的時候就自動釋放(OC brk)
     用戶級內存管理
   1)malloc
   2)calloc
   3)realloc
     C語言執行過程中,在內存中的情況:
   1)代碼段(靜態區域)
     代碼段由程序中的機器碼組成,C語言中源代碼編譯後就形成了機器碼,執行的時候,CPU的程序計數器指向了代碼段的每一條代碼,並由CPU依次執行
   2)數據段(靜態區域)
     只讀數據段,是程序使用一些不會被修改的數據,使用這些數的方式類似與查表式的操作,放置在只讀存儲器中;
     已初始化讀寫數據段,是在程序中聲明,有初值的變量,需佔用寄存器空間,在程序執行時,位於可讀寫的內存區域內,供程序運行時讀寫
   3)BSS段(未初始化讀寫數據段)(靜態區域)
     是在程序中聲明,沒有初始化的變量,這些變量在程序運行前不佔存儲器空間
   4)堆空間(動態區域)
     只在程序運行時出現,一般由程序員分配和釋放,在具有操作系統的情況下,若程序員忘記釋放,在程序結束後,系統會自動回收內存
   5)棧內存(動態區域)
     只在程序運行時出現,在函數內部使用的變量,函數參數及返回值,函數調用(遞歸)都將使用棧空間.棧空間是由編譯器自動分配和釋放的內存.

     C語言提供的兩種內存分配的方式:
   1).靜態內存分配(棧內存)
      由編譯器自動爲我們分配的內存空間,一般棧內存的大小爲10M~30M
      內存的對齊:是指整型佔4個字節,大於4個字節的數據類型,也按照4個字節來計算,而且是4的整數倍
      內存的補齊:只在結構體中有,如果所佔字節數不足4個數據類型,比如char,需將不足的字節數補空,補夠4個字節
   2).動態分配內存(malloc向堆申請內存)
      malloc函數(分配後不初始化)
       void *malloc(size_t size);    //返回值是萬能指針,在Mac下佔8個字節,在Win下佔4個字節
       用於動態的向堆內存申請內存空間,使用完後,要使用free()函數釋放該內存
       一旦指針p指向動態分配的內存塊,就可以忽略p是指針的事實,並且把它用作數組的名字,p[i].
      free函數
      calloc函數(分配之後初始化爲0)(用於存儲構造類型或複合類型數據)
       void *calloc(size_t nmemb, size_t size);   //有nmemb個元素,每個元素佔size個字節,nmemb爲1表示爲任何的數據類型
      realloc函數
       void *realloc(void *ptr, size_t size);      //ptr指向通過malloc或calloc或realloc分配的內存塊
28.預處理
   預處理器的行爲是由指令控制的,這些指令是由#開頭的一些命令.指令總是在第一個換行符處結束,若想在下一行繼,在當前行末尾用\字符.
   【宏定義】 #define指令定義一個宏, #undef指令刪除一個宏定義.
   【文件包含】#include導致一個指定文件的內容被包含到程序中.
     1)#include <文件名>    //用於C語言自身庫的頭文件
     2)#include "文件名"    //用於所有其他頭文件,也包含任何自己編寫的文件
     3)#import <文件名>  "文件名"    //被包含的頭文件只被編譯一次,不會重複
   【條件編譯】#if, #ifdef, #ifndef, #elif, #else和#endif指令將一段文本塊包含到程序中或排除在程序之外.
     1)#if 常量表達式    //當預處理器遇到#if時,如果表達式的值爲0,在#if和#endif之間的行會在預處理過程中從程序中刪除,
       語句              否則,在它們之間的行會被保留在程序中,並繼續被編譯器處理,這時的#if和#endif對程序無任何影響.
       #endif          對於沒有定義過的標示符,#if會把它當做是值爲0的宏對待
     2)defined    僅用於預處理器,當defined應用於標示符時,若標示符是個定義過的宏返回1,否則返回0.常用在#if指令中
       #if defined(BULL)
       ...
       #endif
     3)#ifdef 標示符        //等價於   #if defined(標示符)

       #ifndef
     4)#elif  表達式1
       #else
   【特殊】#error, #line, #pragma更爲特殊,較少用到.
     1)#error 消息
     2)#line n ("文件名")    //line指令用於改變給程序行編號的方式,n在1和32767之間,之後的行編號爲n+1,文件名可以沒有
     3)#pragma 記號        //對非常大的程序或需要使用指定編譯器的特殊功能的程序很有用

29.鏈表
      由一連串的結構(結點)組成的,其中每個結點都包含指向下一個鏈中結點的指針,最後的一個結點包含一個空指針.
   for (p = first; p!=NULL; p = p->next)

30.文件
   C程序中的流的訪問是通過文件指針實現的,類型爲FILE *fp;文件結束符EOF(由宏定義的);
  1)打開文件
   FILE *fopen(const char *filename, const char *mode);    //filename可以包含路徑信息,沒打開返回空指針
   打開的模式(mode):
      "r":只讀            "w":寫,文件不需要存在        "a":追加,文件不需要存在
      "r+":讀和寫,從文件頭開始        "w+":讀和寫,文件存在就截取        "a+":讀和寫,文件存在就追加    
      如果打開的是二進制文件,需要在模式字符串中包含字母b, 即"rb"
  2)關閉文件
   int fclose(FILE *stream);    //參數必須爲文件指針,指針來自fopen或freopen,成功關閉文件返回0,否則返回EOF
  3)爲流附加文件
   FILE *freopen(const char *filename, const char *mode, FILE *stream);    //stream有stdin,stdout,stderr
  4)格式化輸入/輸出
   fscanf(fp,"%d", &i)    //從fp中讀入    fscanf(stdin, "%d", &i)等價於scanf("%d", &i)
   fprintf(fp, "%d", i)    //從fp中讀出    fprintf(stdout, "%d", i)等價於printf("%d", i)
  5)字符讀寫函數
   fgetc(fp)    getc(fp)
   fputc(ch, fp)     putc(ch, fp)
  6)行的輸入/輸出
   fgets(char *s, int n, FILE *stream)    //n爲限制要讀入字符的數量, 爲字符串的時候要額外的加1, 表示爲'\0'        
   gets(char *s)    //逐個讀取字符
   fputs(const char *s, FILE *stream)    //系統不會自己寫入換行符, 可以人爲的加入"\r\n"
   puts(const char *s)    //系統會自動寫入換行符
  7)塊的輸入/輸出(主要用於操作二進制文件)
   fread()
   fwrite()
  8)fflush(fp)    //爲fp清洗緩衝區        fflush(NULL)    //清洗全部緩衝區
    remove("filename")    //刪除文件,參數是文件名,而不是文件指針
    rename("filename", "newfilename")    //重命名文件名, 參數也是文件名,不是文件指針, 文件此時要是關閉狀態
    fcopy  f1.c  f2.c    //把文件f1.c複製給文件f2.c
    feof(fp)
    ferror(fp)
    fseek    //文件位置  文件開始: SEEK_SET    文件當前位置: SEEK_CUR      文件結尾: SEEK_END

31.gcc編譯的過程
  預處理(Pre-processing):-E    
   編譯器將C源代碼中包含的頭文件如stdio.h編譯進來(.i)
  編譯(Compliling):-S
   編譯器首先檢查代碼的規範性, 是否有語法錯誤等, 以確定代碼實際要做的工作, 無誤後, 將代碼翻譯成彙編語言(.s)
  彙編(Assembling):-c
   把編譯階段生成的.s文件轉成二進制目標代碼(.o)
  鏈接(Link):
   將輸出文件.o鏈接成最終的可執行文件

32.函數庫
  靜態庫(.a): 是指在編譯鏈接時, 把庫文件中的代碼全部加入到可執行文件中, 因此生成的文件比較大, 但在運行時就不再需要庫文件了.
  動態庫(.so): 在編譯鏈接時, 並沒有把庫文件的代碼加入到可執行文件中, 而是在程序執行時由運行時鏈接文件加載庫, 節省了系統開銷.
  gcc編譯時, 默認使用動態庫.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章