C語言自學《八》---- C語言知識總結

1、函數

  • 什麼狀況下須要定義一個函數?
    • 經常使用的功能
    • 重複的功能
    • 低效率的代碼
  • 一個函數能夠沒有參數

void  test(void){ //void能夠不寫
    //函數體中不用寫返回函數(return)
}
  • 一個函數能夠沒有返回值,若是沒有定義,默認是返回int類型
test(int a, int b){ 
    //返回值類型能夠不寫,若是不寫,默認爲返回int類型
}
  • 在沒有接觸指針前,函數中的形參是修改不了實參的值的
  • 函數從源文件自上往下執行編譯,因此在調用函數以前,須要聲明函數
  • 函數內部定義不能和形參同名的變量
  • 默認狀況下,不容許兩個同名的函數出現
  • 若是函數有聲明,沒有定義,編譯不會報錯,連接會報錯,由於連接會檢測函數是否存在
  • return函數的做用
    • 退出函數
    • 返回一個具體值給函數調用者
  • 函數不強制要求返回,雖然聲明瞭返回值類型,也無濟於事
  • 什麼叫作連接?
    • 把項目中全部相關聯的」.o」文件和C語言函數庫,合併在一塊兒,生成可執行文件,期間會檢查可能發生的錯誤,若是出現錯誤,就會回到編寫代碼的步驟,修改相應錯誤代碼後,再執行流程
  • 函數的定義和聲明
    • 函數的定義放在」.c」文件中
    • 函數的聲明放在」.h」文件中
  • main函數返回值0表明正常退出,返回其餘值表明異常退出,系統會記錄日誌
  • printf()函數其實也有返回值,它返回字符串的字節數,在當前的編譯器中,中文佔3個字節2、指針
  • 定義和初始化指針
int a = 10; //定義了一個int類型的變量
int *pa  = NULL; //定義了一個int類型的指針
pa = &a; //將int類型變量a的地址賦值給pa指針,或者說pa指針指向了變量a
*pa = 20; //經過指針簡介修改變量a的值,*稱爲取消引用運算符或間接運算符

2、指針

  • 指針只能指向相同類型的變量而且指針只能存儲地址
  • 任何指針都佔用8個字節的存儲空間,固然也受編譯器的影響
  • 經過指針就能夠利用形參修改實參的值,由於指針指向的是地址
  • 指針字符串

//使用char數組存儲字符串,這個叫作字符串變量
char str1[] = 「luoguankun」;
//使用指針存儲字符串,它指向字符串的首字母
//這個稱爲字符串常量,若是再聲明一個字符串值同樣的指針字符串
//那麼他們會指向同一 個字符串,而不會建立一個新的
char *str2 = 「luoguankun」;
  • %s
    • 上面的輸出語句中的%s是字符串輸出控制格式符
    • 它從第一個字符開始直到遇到’\0’終止符號結束(不包括)
  • 內存
    • ——就是那些由編譯器在須要的時候分配,在不須要的時候自動清除的變量的存儲區。裏面的變量一般是局部變量、函數參數等
    • ——就是那些由new分配的內存塊,他們的釋放編譯器不去管,由咱們的應用程序去控制,通常一個new就要對應一個delete。若是程序員沒有釋放掉,那麼在程序結束後,操做系統會自動回收
    • 自由存儲區——就是那些由malloc等分配的內存塊,他和堆是十分類似的,不過它是用free來結束本身的生命的
    • 全局/靜態存儲區——全局變量和靜態變量被分配到同一塊內存中,在之前的C語言中,全局變量又分爲初始化的和未初始化的,在C++裏面沒有這個區分了,他們共同佔用同一塊內存區
    • 常量存儲區——這是一塊比較特殊的存儲區,他們裏面存放的是常量,不容許修改(固然,你要經過非正當手段也能夠修改,並且方法不少)
  • \0
    • 只有擁有’\0’終止符,才說明這是一個字符串,字符是沒有的
    • \0 的ASCII碼值爲0,也就是false
  • 指針數組
    • 數組就是地址
int *name[5]; 
name[1] //取出元素下標爲1的元素(指針)
  • 字符串數組(兩種方式定義)
    • char *name[10]      //10個字符串
    • char name[2][10]    //二維字符數組,可存放2行字符串,每行有10個字符
  • 指向指針的函數
//定義了一個加法函數
int sum(int a, int b){

return a+b;
}

//將定義好的指針指向函數
//首先聲明一個指針
int (*p)(int,int);     //其中(*p)表明未來指向的函數,(*p)左右分別爲返回值類型和形參定義
p = sum; //將指針p指向test函數
p(10,22); //經過指針調用函數,它等於sum(10,22);固然在調用以前別忘了聲明函數
  • 可使用sizeof()運算符計算字符串長度(計算的是字節數)
    • sizeof()的結果類型是size_t類型,它在頭文件中用typedef定義爲unsigned int類型
    • sizeof()運算符能夠用任何類型作參數,甚至函數
  • 也可使用strlen()函數計算字符串長度
    • strlen()只能用char*作參數,且必須是以’’\0’'結尾的
    • 要想使用須要引入<string.h>頭文件
  • 變量類型
    • 全局變量
      • 定義:在函數外面定義的變量叫作全局變量
      • 做用域:從定義變量的那一行開始,到文件結尾
      • 生命週期:當程序啓動開始分配存儲空間,到程序退出纔會被銷燬
      • 初始值若是未定義就爲0
    • 局部變量
      • 定義:在函數代碼塊內部定義的變量
      • 做用域:從定義變量的那一行開始到代碼塊結束
      • 生命週期:從定義變量開始分配存儲空間,代碼塊結束後就會被回收
      • 初始沒有肯定的值

3、結構體

數組只能存儲一組同類型的數據,而結構體能夠保存一組不一樣類型的數據 程序員

  • 定義結構體類型

//如下定義沒有分配存儲空間,僅僅定義類型 算法

struct Person {
   int age;
   int sex;
   char *name;
};
  • 定義結構體變量 數組

struct Person p;
  • 賦值
p.age = 13; //一種方式

//另外一種方式,初始化的同時進行賦值,同時進行分配存儲空間
//結構體的存儲空間是最大成員字節數的倍數,稱爲補齊算法
struct Person p = {13,1,」luoguankun」};

p = {13,1,」luoguankun」}; //這是錯誤的!只能在定義結構體時賦值
  • 輸出結構體中定義的變量值
printf(「age=%d,sex=%d,name=%s」,p.age,p.sex,p.name);
  • 另外一種定義結構體變量的方式
//定義類型的同時定義變量,甚至能夠不寫類型名稱,稱爲匿名結構體,它不能被重用
//而且變量名不能重複
struct Student{
    int age;
}stu;
  • 類型不能重複定義,例如上面的Student不能被定義兩次,變量名也同樣不能重複
  • 結構體數組
//定義結構體類型
struct Person{
    int age;
    double height;
    char *name;
}stu;

//定義結構體數組變量並賦值初始化
struct Person stu[3] =
{
    {11,12.1,"o"},

    {12,12.2,"u"},

    {13,12.3,」l"}

};

//循環遍歷結構體數組,並打印
for (int i = 0; i < sizeof(stu) / sizeof(stu[0]); i++) {
   printf("age=%d,height=%.1f,name=%s\n",stu[i].age,stu[i].height,stu[i].name);
}

//stu[i] = {4,1.22,」dkdk」}; //這是錯誤的

stu[i].age = 4; //這樣是正確的
  • 指向結構體的指針
//省略類型定義,如上,下面使用結構體變量並賦值初始化
struct Person stu = {11, 1.73, "羅冠坤"}; 

//定義了一個指針p並指向結構體變量stu
struct Person *p = &stu;

//三種輸出方法均可以輸出數據,做用相同,其中p->成員名的方式較爲新穎
printf("age=%d,height=%.2f,name=%s\n",stu.age,stu.height,stu.name);
printf("age=%d,height=%.2f,name=%s\n",(*p).age,(*p).height,(*p).name);
printf(「age=%d,height=%.2f,name=%s\n",p->age,p->height,p->name);

//賦值方法的調用方法相同
  • 嵌套結構體

int main(){

    struct Date{
        int year;   //年
        int month;  //月
       int day;    //日
    };

    struct Student{
        int no; //學號
       //嵌套兩個Date來分別表示生日和入學日期
        struct Date birthday;   //生日
        struct Date SchoolDay;  //入學日期
    };

    struct Student stu =
    {
        1,
        {1989,10,24},
        {2009, 9, 1}
    };

    //間接取值,嵌套訪問
    printf("學號爲:%d\n生日爲:%d年%d月%d日\n入學日期爲:%d年%d月%d日\n",
    stu.no,stu.birthday.year,stu.birthday.month,stu.birthday.day,
    stu.SchoolDay.year,stu.SchoolDay.month,stu.SchoolDay.day);

return 0;

}

4、預處理指令

  • 全部預處理指令都是以#開頭
  • 不帶參數的宏定義
//宏名  值;
#define


//宏的變量名所有是大寫,結尾不須要寫分號
#define  COUNT  6


//還能夠取消宏的定義
#undef COUNT
  • 編譯器的做用是將原代碼文件中的代碼翻譯成機器可執行的代碼,也就是0和1,而預處理指令會在編譯器翻譯代碼以前執行
  • 帶參數的宏定義
/*

必定要加括號,不然會出現意想不到的結果好比像下面這樣調用

  sum(10,10) * sum(10,10);

至關於下面:

  10+10*10+10   這樣替換事後改變了運算順序,也就改變了預期的運算結果

  因此必定要把全部變量都加上括號

  再好比平方的例子必定要像下面這樣寫,每一個形參都要加上括號

  #define Square(a)((a)*(a))

*/



#define sum(v1,v2)((v1)+(v2))



int main(void){

    int result = sum(11,10);
    printf("result=%d\n",result); //輸出了21

   return 0;
}
  • 宏定義只是單純的把左邊的定義(好比:sum(v1,v2))替換成右邊的(好比:(v1)+(v2)),不作任何運算

  • 在常見的比較簡單的函數可使用宏定義,這樣比函數效率要高

  • 條件編譯
#define A 10

int main(){

//條件編譯判斷若是用到常量值,好比下面的A
//則必須得是經過宏定義的,由於在編譯前已經進行了判斷
//條件的括號能夠省略    
    #if (A == 10)
        printf("a = 10\n");
    #elif (A == 5)
        printf("a = 5\n");
    #else
        printf("a is other number\n");
    #endif //必定要有#endif結尾

    return 0;

}
  • 防止文件屢次被包含

當進行多文件開發時,某些函數功能,須要在.h文件中進行聲明,還要將.h文件包含到某個文件中,當代碼量過大時,有可能發生屢次包含,這雖然不會產生錯誤,可是會影響性能,因此在頭文件中能夠利用條件編譯,防止屢次包含頭文件,例如像下面這樣定義頭文件: 函數

/*
解釋下面的寫法的邏輯:
若是沒有定義宏 ABC 
那麼就定義一個宏ABC
而且聲明sum()函數
若是第二次被包含時,一樣會進行判斷
此時判斷的條件不成立,由於第一次被包含時已經建立了宏變量ABC
因此這樣一來,避免了重複包含同一個頭文件
ABC宏名稱不能和別的頭文件中的衝突!因此通常使用當前.h頭文件名稱命名
*/

// #ifndef等同於#if !define,對應的有#ifdef 等同於 #if define
#ifndef ABC
// ABC通常寫成當前頭文件的名稱,後面的值隨便寫
    #define ABC 11
    int sum(int,int);
#endif
  • typedef
    • 定義類型的別名(通常是全局變量,方便被使用),使用typedef之後,會提升開發效率
    • 應用於5個場合
  1. 基本數據類型
  2. 指針
  3. 枚舉
  4. 結構體
  5. 指向函數的指針
  • 應用於基本數據類型
//須要分號
typedef int MyInt;

int main(){
    //聲明別名後,能夠這樣定義int類型變量
    MyInt i = 10;
}
  • 應用於指針
//給指針類型起了一個別名String
type char* String;

int main(){
    String = 「luoguankun」;
}
  • 應用於結構體
//定義了一個結構體類型Student
struct Student {
    int age;
};

//給結構體Student起了個別名叫作Mystu
typedef struct Student MyStu;

//或者像下面這樣,在定義結構體類型的時候直接起別名,這樣更加精簡
typedef struct Student {
    int age;
}MyStu;

//若是像上面這樣給結構體起了別名,定義結構體變量就變成了下面這樣:
MyStu s1;
MyStu s2;
MyStu s3;


//還有一種是沒有類型名的結構體
//下面的結構體不能使用本來的方式建立結構體變量
//只能經過下面的方式建立結構體變量,沒法用struct Student stu = {10};這種方式建立結構體變量,
//而前面幾種兩種建立結構體變量的方式均可以
//而這個只能像下面這樣建立:
//MyStu stu;
typedef struct {
    int age;
}MyStu;
  • 應用於枚舉類型

//如下是沒有使用typedef定義別名時的枚舉使用
enum Sex {Man, Woman};
enum Sex s = Man;

//如下是使用typedef定義別名後的使用方法
typedef  enum  Sex MySex;
MySex s = man;

//還能夠在定義枚舉類型的同時定義別名(推薦這樣定義)
typedef  enum Sex {Man, Woman} MySex;
MySex s1 = man;
  • 應用與指向函數的指針

//定義一個函數
int sum(int a, int b){
    return a+b;
}
//指向上面函數的指針聲明和調用
int (*p)(int, int) = sum;
int result = p(10,20);

//使用typedef爲指向函數的指針定義別名
typedef int (*MyPoint)(int, int);

MyPoint p = sum;
int result = p(20,20);
  • 簡化指向結構體的指針

//簡化前
struct Person{
    int age;
}; 

struct Person p = {20}; 
struct Person *p2 = &p;
printf("age = %d\n」, p2->age);



//簡化後
typedef struct Person{
     int age;
} *PersonPoint;

struct Person p = {20};    
PersonPoint p2 = &p;
printf("age = %d\n", p2->age);

5、static和extern對函數的做用

  • 這兩個關鍵字分別表明內部和外部函數
    • static —— 內部函數,只能被本文件訪問,其餘文件不能訪問,但能夠經過外部函數間接訪問
    • extern —— 外部函數(默認)
  • 在多文件開發的狀況下,不一樣文件中的外部函數不能夠重名,內部函數能夠同名,但在同一個文件中內外部函數都不能重名
  • 定義完整的外部函數須要在函數前加上extern關鍵字,也能夠省略,由於它是默認的
  • 定義內部函數須要在函數前面加上static關鍵字
  • 雖然被static關鍵字修飾的函數不能被外部文件直接調用,但能夠間接調用,在外部函數中調用內部函數(條件是要被間接調用的內部函數和外部函數在同一個文件),而且必須在外部函數的上面被聲明


6、static和extern對變量的做用

  • 全局變量分爲兩種:
    • 外部全局變量——默認,類型關鍵字前面無需加任何修飾符
    • 內部全局變量——被static修飾符修飾
  • 默認狀況下,全部的全局變量都是外部變量,不一樣文件中的同名變量,都表明同一個變量
  • 內部變量:只能被本文件訪問,不能被別的文件訪問,不一樣文件同名的內部變量,互不影響
  • static對變量的做用:定義一個內部變量
  • extern對變量的做用:聲明一個外部變量
  • static對函數的做用:定義和聲明一個內部函數
  • extern對函數的做用:定義和聲明一個外部函數(能夠省略)


6、static對局部變量的做用 性能

  • 延長局部變量的生命週期直到程序結束
  • 期間並無改變局部變量的做用域
  • 使用場合:
    • 某個變量的值固定不變,而被調用的次數又很是多,就應在變量前加上static修飾符
相關文章
相關標籤/搜索