C 基礎

C

c是一種介於高級語言與低級語言之間的語言,將原文最終編譯爲機器碼執行的。程序員

  1. 編譯工具 MinGW-W64 GCC
  2. 配置庫環境 vscode 設置 "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
  • gcc 工具解析
指令 結果 文件 備註
-E 生成預編譯文件 tmp.i 手動指定纔可生成文件,>-o
-S 生成彙編文件 tmp.s 自動生成文件
-c 生成機器碼文件 tmp.o 自動生成文件
-o 指定生成文件 tmp.exe 指定生成文件
null 生成可執行文件 tmp.exe 自動生成文件

變量

c語言的變量皆爲強類型限定,且全部變量只要定義完成,其內存空間便已申請完成。windows

boolean

全部數值型變量(基礎類型變量)以及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

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 的標準庫文件不多,可是確實強大。

經常使用方法

  • 內存管理

此處全部的內存管理都是基於堆內存的處理。

  1. void* malloc(int size) 申請 size 大小的空間,並不初始化。

  2. void* calloc(int num,int size) 申請 num 個 size 大小的連續空間,且每一個字節都被初始化爲 0。

  3. void* realloc(void* addr,int new_size) 對 addr 從新分配內存,新空間大小爲 new_size 。

  4. 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類型數組,和堆很類似。

  1. strcpy(s1,s2)
  2. strcat(s2,s2)
  3. strcmp(s1,s2)
  4. strlen(s)
  5. strchr(s,ch)
  6. strstr(s1,s2)
// char 轉爲string
char p[] = {'1','2','3','\0'};
puts(p);

// 有效長度是4
char q[] = "4321";
printf("%s",q);
  • 輸入輸出
  1. prinf("hellow %d",p) scanf("%d",p)

  2. puts(s) gets(s)

  3. char a = getchar() putchar(a)

  4. fprintf() fscanf() 文件IO

  • 其餘庫
  1. stdio.h
  2. stdlib.h
  3. string.h
  4. math.h
  5. error.h
  6. ctype.h
  7. ......
  • others
  1. system("cls")
  2. system("pause")
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() ...... ,
相關文章
相關標籤/搜索