程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 快速學習C語言一: Hello World

快速學習C語言一: Hello World

編輯:關於C語言

快速學習C語言一: Hello World


估計不會寫C語言的同學也都聽過C語言,從頭開始快速學一下吧,以後肯定能用的上。 如果使用過其它類C的語言,如JAVA,C#等,學C的語法應該挺快的。   先快速學習並練習一些基本的語言要素,基本類型,表達式,函數,循環結構, 基本字符串操作, 基本指針操作,動態分配內存,使用結構表示復雜數據, 使用函數指針實現靈活邏輯。   雖然C是一個規模很小的語言,但也得自己多設計一些練習練手才能學會。   基本類型 我就記得char, int, 別的都不常用吧應該,用的時候再搜索。   表達式 和JAVA, C#差不多吧,不用學基本,各種算數運算符,關系運算符,邏輯運算符,逗號, 括號等的意思應該也差不多,表達式最終的結果也有類型和值。   函數 函數是最基本的抽象,基本沒有什麼語言沒有函數的概念,它封裝一系列操作, 最簡單的Hello world,如下。     static void hello_world(){     printf("hello, world\n"); }     我們的練習都是隨手寫的函數,不需要被外部調用,所以前面加個static,表示只在 本文件內可見。   printf輸出一行的話,最後要加\n, 常見個格式化參數有%d,%c,%s,%p等,分別表示 輸出int, char, 字符串, 指針。   分支,循環結構 和別的語言差不多,不過i的聲明要放在函數開頭,c89就是這樣。   static void n_hello_world(int n){     int i = 0;     for (i = 0; i < n; i++) {         printf("hello, world\n");     } }     字符串練習,獲取一個字符串的長度 庫函數strlen就是干這個的,不過我們自己可以寫一個練手,c沒有字符串類型, 用'\0'結尾的字符數組表示字符串,所以for循環從頭滾到'\0'位置就好了。   復制代碼 // 字符串練習, 計算字符串長度 static int w_strlen(const char* str){     int i;     // 向後滾動指針,同時遞增i,直到找到字符串結尾     for (i = 0; *str != '\0'; str++, i++) {         ;     }     return i; } 復制代碼     const 修飾符表示這個參數不能在函數裡進行更改,防止意外改動。char *就是傳說中 字符串了。 寫C程序得用好for語句,有各種慣用法,用好了可以寫出很緊湊的程序,比如上面for語句 的第2個分號後的逗號表達式可以遞增兩個變量。   理解字符串的存儲 第一種方式是在編譯時分配的內存,是字符串常量,指針s1指向的內存不能更改。 第二種方式應該是在棧上分配的內存(不確定),可以通過指針修改其中的字符。   復制代碼 static void change_str_test(){     // 常量不能修改     // char* s1 = "hello"; // will core dump     char s1[10] = "hello";     *s1 = 'p';     printf("%s\n", s1); } 復制代碼     指針練習 指針可以進行加減的操作,每加一次就滾動過它指向的類型長度, 比如char指針就是 滾動1個字節。   復制代碼 // 指針練習, 反轉字符串 static char* reverse(char* str){     char* ret = str;     // 滾到字符數組末尾的\0之前     char* p = str + w_strlen(str) - 1;     char c;       // 兩個指針,一個從前往後滾,一個從後往前滾,直到就要交錯之前     // 滾動的過程中交換兩個指針指向的字符     for ( ; p > str; --p, ++str) {          printf("debug[reverse]: %p %p %c %c\n", p, str, *p, *str);         c = *p;         *p = *str;         *str = c;     }       return ret; } 復制代碼     c = *p表示取出指針p指向的字符,賦值給變量c,*表示取值。   *p = *str相當於p[i] = str[i],右邊的取出來的是值,左邊的取出來的也是值, 值賦值給值,看起來有些詭異,但就是這樣寫的。反正p = *str肯定不對,因為p是 指針類型,*str是計算結果是字符類型。   動態分配內存 我記得TCPL前幾章都沒講malloc,free等內存分配的函數,好多任務只需要在編譯階段 分配內存就夠了,但比較大型復雜的程序應該都需要動態管理一些內存的。   C語言沒有GC,要手工釋放動態分配的內存,否則就會造成內存洩漏,所以一定要配平 資源,有malloc的地方,一定要想好它應該在哪裡free。   目前我了解到的原則就有兩種:   誰分配,誰釋放 誰使用,誰釋放 對了, malloc出來的內存要強轉成你需要的指針類型,然後free時指針要滾到你動態 分配內存的起始點。   復制代碼 // 內存申請相關,連接兩個字符串 static void concat_test(){     char* a = "hello";     char* b = "world";     //結果字符串長度為兩個字符竄長度加\0的位置     int len = w_strlen(a) + w_strlen(b) + 1;     // 動態分配內存     char* p = (char *)malloc(sizeof(char) * len);     char* result;        // 必須判斷是否分配到內存     if (p != NULL){         // 保存動態分配內存的開始指針,free時必須從這裡free         result = p;           //滾動p和a,直到a的末尾         while (*a != '\0') {             printf("debug[concat_test]:while a %p %c\n", a, *a);             *p++ = *a++;         }           //滾動p和b,直到b的末尾         while (*b != '\0') {             printf("debug[concat_test]:while b %p %c\n", a, *a);             *p++ = *b++;         }           // 末尾整個0         *p= '\0';         printf("concat_test: %s\n", result);           //釋放動態分配的內存         free(result);     }else{         printf("malloc error");      } } 復制代碼     結構練習 C沒有類,要表達復雜的數據,就得用結構了, 結構也可以用指針來指,如果是結構變量 的話,引用成員用.,如果是指向結構的指針,引用成員用->   別的好像沒啥特別的,注意動態分配結構數組後,指針滾動的邊界,別使用了界外的 內存。如果結構的成員指向的內存是動態分配的花,也記得free。   沒有結構,估計寫不出大程序,結構應該會用的很多。   復制代碼 //結構練習,人員統計系統 struct customer {     char* name;     int age; };   static void customer_manager() {     // 直接在棧上分配結構體     struct customer wawa;     struct customer* p_wawa;     struct customer* p_customers;     int n = 2;       char name[] = "wawa";     // char* name = "wawa"; //splint warning     char name2[] = "tiancai";       // 直接用結構名訪問成員      wawa.name = name;     wawa.age = 30;     printf("%s is %d years old\n", wawa.name, wawa.age);       // 用指針訪問結構成員     p_wawa = &wawa;     p_wawa->age = 31;     printf("%s is %d years old\n", wawa.name, wawa.age);       // 為員工數組動態分配內存     p_customers = (struct customer*)malloc(sizeof(struct customer) * n);     if (p_customers != NULL) {         // 設置數組第一項         p_customers->name = name;         p_customers->age = 10;           // 設置數組第二項         p_customers++;         p_customers->name = name2;         p_customers->age = 30;           // 滾動數組外面,然後反向循環到數組開始         p_customers++;         while(n-- > 0){             p_customers--;             printf("%s is %d years old\n", p_customers->name, p_customers->age);         }           // 釋放動態分配的內存,這時候p_customers已經位於起始位置了         // 結構體裡的name1, name2是在棧上分配的,不用釋放         free(p_customers);     } } 復制代碼     函數指針練習 好多語言都有高階函數的特性,比如函數的參數或返回值還可以是個函數, C裡也有函數指針可以達到類似的效果,用來做回調函數等。   但C的函數指針寫起來比較詭異,不好記憶,不行就用typedef來重新命個名,寫起來 簡單一些。   下面用一個比較經典的冒泡排序來演示函數指針的使用,傳遞不同的比較函數可以 改變排序函數的行為,這是寫復雜靈活邏輯的一種很方便的方式。   復制代碼 // 函數指針練習, 排序   // 正序排序的比較函數 static int cmp_default(int a, int b){     return a - b; }   // 反序排序的比較函數 static int cmp_reverse(int a, int b){     return b - a;  }   // int類型的冒泡排序算法,可傳入一個比較函數指針 // 類似回調函數,該函數需要兩個int參數且返回int static void sort(int* arr, int n, int (*cmp)(int, int)){     int i, j, t;     int *p, *q;       p = arr;       for (i = 0; i < n; i++, p++) {         q = p;         for (j = i; j < n; j++, q++) {             // 調用函數指針指向的函數和使用函數一樣,貌似是簡單寫法             if (cmp(*p, *q) > 0) {                 t = *p;                 *p = *q;                 *q = t;             }         }     } }   // 測試排序函數 static void sort_test(){     int arr[] = {4, 5, 3, 1, 2};     int i, n = 5;       // 正向排序, 傳入cmp_default函數的地址,貌似不需要&取地址     sort(arr, 5, cmp_default);     for (i = 0; i < n; i ++) {         printf("%d%s", arr[i], i == n - 1 ? "" : ", ");      }     printf("\n");       //反向排序,同上     sort(arr, 5, cmp_reverse);     for (i = 0; i < n; i ++) {         printf("%d%s", arr[i], i == n - 1 ? "" : ", ");      }     printf("\n"); }

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved