c是一種介於高級語言與低級語言之間的語言,將原文最終編譯爲機器碼執行的。程序員
"C_Cpp.updateChannel": "Insiders"
#
爲c語言之中的編譯符號
gcc
爲基本編譯工具spring
預編譯shell
預編譯是指在編譯以前對原文所作的預處理,生成 tmp.i
文件。編程
- 文件預編譯:引入
include<exp.h>
(系統庫),include"exp.h"
(自定義庫) 等頭文件。- 條件預編譯:
#if
根據條件執行特定編譯。- 常量預編譯:
#FLAG
替換預編譯常量。- 刪除代碼註釋
- 將原文編譯爲彙編語言,生成
tmp.s
文件。- 將原文編譯爲機器碼,生成
tmp.o
文件。- 將原文編譯連接生成最終可執行文件,
tmp.exe
文件。
# 將tmp.c編譯爲可執行文件tmp.exe windows平臺默認爲.exe文件 gcc ./tmp.c -o tmp.exe # 將tmp.c預編譯爲tmp.i gcc -E ./tmp.c -o tmp.i # 將tmp.c預編譯並重定向到tmp.txt gcc -E ./tmp.c > tmp.txt # 將tmp.c編譯爲彙編 gcc -S ./tmp.c gcc -S ./tmp.c -o tmp.s # 將tmp.c編譯爲obj文件 gcc -c ./tmp.c gcc -c ./tmp.c -o tmp.o
指令 | 結果 | 文件 | 備註 |
---|---|---|---|
-E | 生成預編譯文件 | tmp.i |
手動指定纔可生成文件,> ,-o |
-S | 生成彙編文件 | tmp.s |
自動生成文件 |
-c | 生成機器碼文件 | tmp.o |
自動生成文件 |
-o | 指定生成文件 | tmp.exe |
指定生成文件 |
null | 生成可執行文件 | tmp.exe |
自動生成文件 |
c語言的變量皆爲強類型限定,且全部變量只要定義完成,其內存空間便已申請完成。windows
全部數值型變量(基礎類型變量)以及NUll,可安全做爲boolean。
完成空間申請的指針爲true,未完成空間申請的指針會出 error。數組
boolean | 標識範圍 |
---|---|
true | 非0數值,成功定義的任意級指針 |
false | 0,NULL |
c語言是一種塊級代碼,某一塊內部變量外部是沒法使用的,函數,以及循環體頭部變量在外部是沒法使用的。緩存
關鍵詞 | 名稱 | 做用域 | 備註 |
---|---|---|---|
#define |
宏變量 | 文件內部 | #define ~ #undef 或至文件結尾 |
auto |
塊變量 | 當前快結構,或某些循環體頭部 | 變量默認都是 auto |
register |
寄存器變量 | 定義與當前cpu寄存器同寬的值 | 運行時存在位寬限制 |
static |
靜態變量 | 全局變量 | 讀寫皆可生效 |
extern |
引用變量 | 本文件生效 | 引用全局變量,或函數至本文件 |
const |
只讀變量 | 變量可讀不可修改 | 特殊修飾詞,某些函數中的參數限定詞 |
#define
#undef
定義撤銷宏變量,此類型變量會在預編譯時,替換或終止替換指定變量。安全
#define MAX_SIZE 1000 //...... #undef MAX_SIZE
類型 | 符號 | 字節 | 備註 |
---|---|---|---|
char | yes | 1 | 由讀取方式決定類型(small int/char) |
short int | yes | 2 | 簡寫 short |
int | yes | 4 | 最經常使用的整型 |
long int | yes | 8 | 簡寫 long |
float | no | 4 | 經常使用浮點數 |
double | no | 8 | 大浮點 |
結構體 struct數據結構
結構體數據用於各種自定義數據結構。ide
typedef ... Example{...}example; // 全名纔可以使用 struct Example exp; union Example exp; enum Example exp; // 別名使用 example exp;
結構體定義之時,以全部部份內存的最小公倍數爲基本單位(字節爲基礎單位),對結構體內部進行內存分配,且空間大小從上至下遞增而非其餘,其中多出的部份內存會被填充沒法正常使用。即定義數據結構時,基本數據類型儘可能不要變,且內存大小從上至下遞增。
- typedef定義
1. 對變量類型賦予別名
2. 定義結構體類型- 結構體使用
1. 當爲普通方式訪問時用.
2. 當爲指針方式訪問時用->
typedef unsigned char byte; typedef struct List{ byte* base; byte* top; byte* size; }*list; void main(){ int x = 10; printf("輸出:%d",x); scanf("%d",&x); //...... byte y = list->base; byte z = (*list).base; }
枚舉類型 enum
經常使用於映射某些有限分類值,如星期幾,幾月,季節,性別...
一種映射模式,指定某些變量表明某些值,這些變量會變爲常量。
typedef enum Season{ spring = 1, summer, autumn, winter }season; typedef enum Season{ spring = 1, summer = 2, autumn = 3, winter = 4 }season_cmp; void main(){ season sea = spring; printf("The season's number is %d",sea); printf("The season's number is %d",spring); }
位域結構體
經常使用於高頻次二項分佈運算 0~1。
基本類似於結構體,不一樣之處在於位域結構體僅支持(unsigned int / int)的數據類型,以"位"爲基本單位定義數據結構,數據默認存在爲0,但存在BUG,不賦值時慎用。其內部是將int數值拆分做爲一個總體運算的。
// 內存以 int 爲單位,x,y不能超過位上限 typedef struct Flag{ unsigned int x:1; //0~1 unsigned int y:2; //0~3 }flag;
共用體 union
經常使用於高速緩存數據,或短期記錄某些值。
基本結構類似於結構體,惟一不一樣的是,其本省內存佔用量就是最大數據的內存佔用量,即同一時間裏,通常僅爲最近一被改寫的數據是有效的,其餘數據因爲內存結構被覆蓋會致使數據失效。
// 同一時間段僅有一個值有效,且內存爲 4 typedef union Record{ unsigned int x; unsigned int y; unsigned int z; }record;
指針,包括指針空間自己以及所保存的一個地址,這個地址指向一個存儲空間。
符號 | 普通變量 | 指針變量 | 數組變量 |
---|---|---|---|
&p |
原始變量指針化 | 指針變量指針化(多級指針) | |
(int)&p |
存儲空間地址(int) | 指針空間地址(int) | 存儲空間頭地址(int) |
(int)p |
強轉int類型 | 存儲空間地址(int) | 存儲空間頭地址(int) |
*
將任意變量指針化,定義時使用。&
對任意變量取地址,運算時使用。
類型 | 定義 | 內存管理 |
---|---|---|
普通變量 | 自動申請存儲空間 | 棧區自動處理 |
指針變量 | 自動申請指針空間,無存儲空間 | 棧區自動處理,非棧區手動處理 |
利用一個虛擬地址(整數),強制轉化出一個指定類型的指針變量。
int p = 100; int addr = &p; int* q = (int*)addr; // 強制地址轉化指針 int x = *q; int x = *(int*)(&p);
靜態變量爲被
static
修飾的變量,全局變量則爲在程序原文最外側所設置的變量。
靜態區亦稱全局區,由編譯器管理,編譯一次便生效,程序結束失效。
普通變量或稱臨時變量以及函數參數等,以及指針變量都在這裏。
棧區亦稱爲臨時區,由編譯器管理,隨着程序運行而改變。
一些自定義結構體以及大型數據都在這裏操做,配合指針便可靈活使用。
堆區徹底由程序員手動控制,包括內存的申請以及釋放等;需注意的是此處易發生內存的各種錯誤,需謹慎使用。
代碼區由編譯器管理,主要用於存儲函數體的二進制原文。
指針空間泛指一些指針能夠活動的區域,主要包括棧區指針區,部分堆區;按照使用類型又可分爲引用其餘變量的空間,以及使用本身申請的空間。
引用區域 | 內存管理 | 備註 |
---|---|---|
堆區 | 程序設計者管理內存 | 需嚴謹設計以避免發生內存錯誤 |
棧區普通變量 | 自動內存管理 | 普通變量消失時,指針需變爲NULL |
棧區指針變量 | 自動內存管理 | 指針連接指針即爲多級指針 |
int* p; p = (int*)malloc(sizeof(int)); *p = 100; free(p);
int p = 100; int* m; // &p 取一個普通變量的 起始地址 m = &p;
int p = 100; // 一級引用 int* m; m = &p; // 二級引用 int** n; n = &m; **n = 1000;
多級指針
多級指針的內在形式主要包括前面幾個節點所指出的,棧區指針,堆內部指針之間的自我引用以及相互引用。且多級指針在操做上和單級指針別無原理上差異。
多級指針需由內向外或由外向內嵌套操做。
- 單級指針:引用其餘存儲空間,或手動申請堆空間,其後做爲普通變量便可。
- 多級指針:逐級引用下一級別指針,最後將其做爲普通指針使用便可。
數組索引,自己就是一個指針,但其沒有指針空間地址,僅有存儲空間頭地址。
(int)p
以及(int)&p
都是一個數值,都是存儲空間頭地址。
數組,一段連續的內存空間,由定義方式不一樣致使棧區和堆區之中均可以存在。
爲肯定數組存儲空間大小,可在定義階段初始化數組,不然必須給定數組長度。
int p[] = {0,1}; int p[2]; p[0] = 0; p[1] = 1;
數組索引能夠徹底看成指針來使用。
(int)(&p)
與(int)p
均可以正確獲取頭指針位置。
int p[] = {0,1,2,3,4}; int addr = p;
指針運算僅支持
+
-
操做,且位移距離爲指針所指向的空間的大小。當數組作指針運算時,是向上或向下移動一整個數組的位置。
int p[] = {0,1,2,3,4}; // 數組名直接就是地址值 int* q = p; // 向量運算支持自增自減 q++; // 數組的兩種訪問方式 printf("%d %d %d %d %d",p,*q,*(q+1),*(q+1)+1,p[2]);
指針數組是指一個由指針所組成的數組,本質是一個標準數組。
數組指針是指一個數組的索引徹底由一個指針替代,本質是一個標準指針變量。
// 此處是指 p[10] 的 基本組成單位爲 int* int* p[10]; // 此處是指 (*p)[10] 的基本組成單位爲 int int (*p)[10];
多級指針能夠拿來當數組使用。
函數是一種結構化編程的思惟方式。
返回值限定
函數名定義
函數參數定義
實際操做之中,還包括內存管理,異常處理,權限管理等
int example(int x,int y){ return x+y; }
函數的參數定義在編譯階段就已完成,因此參數的地址是固定的,可是參數的地址僅僅函數內部是可讀的,且沒法將函數參數地址從函數之中取出。若是能夠拿出來會出現嚴重的系統性安全問題。
將原始值的一個
copy
傳遞給函數,參數和原始值是徹底分離的。
其參數是一個指針,其是將原始參數的存儲地址賦予參數的存儲地址,保證參數指向原始參數,此時參數的指針空間地址不變,而所保存的指向空間則變爲數據的存儲地址。
void demo_int(int x){ printf("值傳遞-> 參數存儲空間:%d 參數值:%d\n",(int)&x,x); } void demo(int* x){ printf("址傳遞-> 參數指針空間:%d 參數存儲空間:%d 值:%d\n",(int)&x,(int)x,*x); } void main(){ int x = 10; int z = 11; int* y = &x; printf("原始存儲空間:%d 值:%d\n",(int)&x,x); printf("指針存儲空間:%d 值:%d 指針空間:%d\n",(int)y,*y,(int)&y); demo(&x); demo(y); demo(&z); demo_int(x); demo_int(z); }
const 自己就是一個只讀的修飾符。
// 等價於 int const p,p 只讀變量 void demo(const int p){} // 指針不可變,指向的內存空間則無需關心 void demo(const int* p){} // 指針不可變,指向的內存空間依然不可變 void demo(const int* const p){}
全部的邏輯判斷必須保證全部路徑都被覆蓋到,不然會出現嚴重漏洞!
int true = 1; int false = 0; if(true){} if(true){}else{} if(true{}elseif(){}else{} int key = 0; switch(key){ case 1: example; break; //如無此語句,則會直接靜茹下一次循環 case 1: example; break; default: example; //此處通常無需 break }
for語句頭部定義的變量爲for塊級變量。
break
,continue
。
goto 語句,基本不多用。
// for頭部定義的變量爲塊級別變量,僅在for內部有效,且定義可覆蓋外部定義。 for(int i=0;i<=10;i++){ printf("%d",i); } while(true){} do{}while(); STA: goto STA;
函數指針就是將函數引用指針化。
(int)add
(int)&add
均可正確獲取函數的開始地址。
(int)p
(int)&p
前者獲取指針函數所指向函數的地址,後者獲取指針自己的地址。
int add(int x,int y){ return x+y; } void main(){ // 指針函數定義 int (*p)(int,int); p = &add; printf("%d\n",(*p)(10,13)); printf("原函數地址:%d %d\n",(int)add,(int)&add); printf("指針函數地址:%d 指針函數所包含的函數地址:%d\n",(int)&p,(int)p); }
就是返回值是指針類型的函數。
C 的標準庫文件不多,可是確實強大。
此處全部的內存管理都是基於堆內存的處理。
void* malloc(int size)
申請 size 大小的空間,並不初始化。
void* calloc(int num,int size)
申請 num 個 size 大小的連續空間,且每一個字節都被初始化爲 0。
void* realloc(void* addr,int new_size)
對 addr 從新分配內存,新空間大小爲 new_size 。
void free(void* addr)
全部申請的空間都須要從這裏釋放,不及時釋放或丟失空間地址會形成內存泄漏。
c語言的文件管理有種面向對象的感受,但實際就是一個結構體。
r w a r+ w+ a+ 等即爲文件模式的表示符號。
FILE *p = NULL; p = fopen("./example.txt","w"); fclose(p); p = NULL;
字符串
char 用 'c'
表示,string 用 "str"
表示。
c語言的string就是一個char類型數組,和堆很類似。
// char 轉爲string char p[] = {'1','2','3','\0'}; puts(p); // 有效長度是4 char q[] = "4321"; printf("%s",q);
prinf("hellow %d",p) scanf("%d",p)
puts(s) gets(s)
char a = getchar() putchar(a)
fprintf() fscanf() 文件IO
const int* a 與 int const *a 做用相同。此時沒法對 a 指向的變量作修改。 const int* const a 則指所指向的變量與指針自己皆不可修改。 // 此時修飾的是 const (int* a) 故實際是,值沒法改變 const int* a // 此時修飾的是 const (a) 歸實際是指針自己沒法改變 int* const a // 下面這兩個東西有數組和string的影子,會出現BUG,儘可能少使用。 calloc() memset(maze,0,size);
- 算術運算:+ - * / % ++ --
- 關係運算:== != > >= < <=
- 邏輯運算:! && ||
- 位運算 :& | ^ ~ >> <<
- 賦值運算:= += -= *= /= %= <<== >>== &= ^= |=
- 特殊運算:sizeof() & * ( ? : )
位運算是一種二進制對位運算
類型 | 符號 | 1 | 0 |
---|---|---|---|
與運算 | a & b |
a,b全都爲1 | a,b至少有一個0 |
或運算 | a | b |
a,b至少一個1 | a,b全都爲0 |
亦或運算 | a ^ b |
a,b不等 | a,b相等 |
反運算 | ~ a |
a爲0 | a爲1 |
位移運算 >> << 則是直接執行位移操做,0用以補位
c語言之中存在不少的運算符,各個運算符衝突之時,會優先執行等級高的運算。
// p++ 等級比較高 再到 ++p () [] -> . ++ -- (type) * & sizeof() ...... ,