一、C簡介
1.1 什么是C語言忘巧?
C語言是一門通用計算機編程語言恒界,C語言的設(shè)計目標(biāo)是提供一種能以簡易的方式編譯、處理低級存儲器砚嘴、產(chǎn)生少量的機器碼以及不需要任何運行環(huán)境支持便能運行的編程語言十酣。
盡管C語言提供了許多低級處理的功能,但仍然保持著良好跨平臺的特性际长,以一個標(biāo)準(zhǔn)規(guī)格寫出的C語言程序可在許多電腦平臺上進行編譯耸采,甚至包含一些嵌入式處理器(單片機或稱MCU)以及超級電腦等作業(yè)平臺。
關(guān)于C
- C 語言是為了編寫 UNIX 操作系統(tǒng)而被發(fā)明的工育。
- C 語言是以 B 語言為基礎(chǔ)的虾宇,B 語言大概是在 1970 年被引進的。
- C 語言標(biāo)準(zhǔn)是于 1988 年由美國國家標(biāo)準(zhǔn)協(xié)會(ANSI如绸,全稱 American National Standard Institute)制定的嘱朽。
- 截至 1973 年,UNIX 操作系統(tǒng)完全使用 C 語言編寫怔接。
- 目前搪泳,C 語言是最廣泛使用的系統(tǒng)程序設(shè)計語言。
- 大多數(shù)先進的軟件都是使用 C 語言實現(xiàn)的扼脐。
- 當(dāng)今最流行的 Linux 操作系統(tǒng)和 RBDMS MySQL 都是使用 C 語言編寫的森书。
1.2 C語言能做什么?
- 操作系統(tǒng)開發(fā)
- 軟件開發(fā)
- 嵌入式開發(fā)
- 語言編譯器
1.3 C語言有什么優(yōu)點谎势?
- 可移植性:C語言是高度可移植的,你在不改動或者只做很小改動的情況下,就可以把C語言的程序運行在不同平臺凛膏;
- C語言很小:C語言完全基于變量,宏命令,函數(shù)和架構(gòu),整體非常小,因此C語言可以嵌入幾乎現(xiàn)代所有微型處理器中,從冰箱到鬧鐘;
- 學(xué)會C學(xué)會一切:幾乎所有編程語言都由C語言實現(xiàn),或者有著和C語言一樣相似的語法和邏輯規(guī)則,因此,學(xué)會C語言能使你很快學(xué)會其他語言脏榆。
1.4 C語言有什么缺點猖毫?
- 運行時間:C語言沒有運行時間檢查機制;
- 面向過程:C語言不支持面向?qū)ο缶幊?這就是為什么創(chuàng)造C++须喂;
- 不安全:指針是C語言的一大特色,可以說是C語言優(yōu)于其它高級語言的一個重要原因,但也就是因為它有指針,可以直接進行靠近硬件的操作,所以帶來很多不安全的因素吁断。
記住:語言終究只是工具坞生,算法才是核心仔役,思路才是靈魂。
二是己、C基本語法
2.1 C 程序主要構(gòu)成
- 預(yù)處理器指令
- 函數(shù)
- 變量
- 語句 & 表達式
- 注釋
2.2 C 程序編寫流程
- 編輯
- 預(yù)編譯
- 編譯
- 匯編
- 鏈接
- 運行
2.3 C語言結(jié)束符
C程序語句的結(jié)束是通過分號‘;’實現(xiàn)的又兵,它表明一個邏輯實體的結(jié)束。
2.4 C語言注釋
C語言的注釋是以/*開始,以*/終止的沛厨。單行的注釋宙地,也可以使用雙斜杠‘//’表示。
您不能在注釋內(nèi)嵌套注釋逆皮,注釋也不能出現(xiàn)在字符串或字符值中宅粥。
2.5 C語言標(biāo)識符
C 標(biāo)識符是用來標(biāo)識變量秽五、函數(shù)集畅,或任何其他用戶自定義項目的名稱房揭。一個標(biāo)識符以字母A-Z或a-z或下劃線_開始眯亦,后跟零個或多個字母梦碗、下劃線和數(shù)字(0-9)就缆。
C 標(biāo)識符內(nèi)不允許出現(xiàn)標(biāo)點字符淆院,比如 @薄风、$ 和 %牢贸。C 是區(qū)分大小寫的編程語言竹观。
2.6 C語言關(guān)鍵字
下列C中的保留字不能作為常量名、變量名或其他標(biāo)識符名稱潜索。
auto | else | long | switch |
---|---|---|---|
case | extern | return | union |
char | float | short | unsigned |
const | for | signed | void |
continue | goto | sizeof | volatile |
default | if | static | while |
do | int | struct | _Packed |
double | define | typedef |
2.7 C語言空格
只包含空格的行臭增,被稱為空白行,可能帶有注釋竹习,C 編譯器會完全忽略它誊抛。
在 C 中,空格用于描述空白符整陌、制表符拗窃、換行符和注釋∶诒瑁空格分隔語句的各個部分随夸,讓編譯器能識別語句中的某個元素(比如int)在哪里結(jié)束,下一個元素在哪里開始震放。
三宾毒、C基礎(chǔ)數(shù)據(jù)
3.1 數(shù)據(jù)類型
C語言中的類型包含以下幾種:
序號 | 類型 | 描述 |
---|---|---|
1 | 基本類型 | 它們是算術(shù)類型,包括兩種類型:整數(shù)類型和浮點數(shù)類型 |
2 | 枚舉類型 | 它們也是算術(shù)類型殿遂,被用來定義在程序中只能賦予其一定的散列整數(shù)值的變量 |
3 | void類型 | 類型說明符void表明沒有可用的值 |
4 | 派生類型 | 它們包括:指針類型诈铛、數(shù)組類型、結(jié)構(gòu)類型墨礁、共用體類型和函數(shù)類型 |
數(shù)組類型和結(jié)構(gòu)類型統(tǒng)稱為聚合類型幢竹;函數(shù)的類型指的是函數(shù)返回值的類型。
為了得到某個類型或某個變量在特定平臺上的準(zhǔn)確大小恩静,可以使用sizeof運算符焕毫。
整數(shù)類型
類型 | 存儲大小 | 取值范圍 |
---|---|---|
char | 1 byte | -128 ~ 127或0 ~ 255 |
unsigned char | 1 byte | 0 ~ 255 |
signed char | 1 byte | -128 ~ 127 |
int | 2或4 bytes | -32,768 ~ 32,767或-2,147,483,648 ~ 2,147,483,647 |
unsigned int | 2或4 bytes | 0 ~ 65,535或0 ~ 4,292,967,295 |
short | 2 bytes | -32,768 ~ 32,767 |
unsigned short | 2 bytes | 0 ~ 65,535 |
long | 4 bytes | -2,147,483,648 ~ 2,147,483,647 |
unsigned long | 4 bytes | 0 ~ 4,294,967,295 |
浮點類型
類型 | 存儲大小 | 值范圍 | 精度 |
---|---|---|---|
float | 4 bytes | 1.2E-38 ~ 3.4E+38 | 6位小數(shù) |
double | 8 bytes | 2.3E-308d ~ 1.7E+308 | 15位小數(shù) |
long double | 10 bytes | 3.4E-4932 ~ 1.1E+4932 | 19位小數(shù) |
void類型:常用于下面三種情況
序號 | 類型 | 描述 |
---|---|---|
1 | 函數(shù)返回為空 | C中有各種函數(shù)不返回值,或者說它們返回空。無返回值的函數(shù)的返回類型為空 |
2 | 函數(shù)參數(shù)為空 | C中有各種函數(shù)不接受參數(shù)咬荷;不帶參數(shù)的函數(shù)可以接受一個void |
3 | 指針指向void | 類型為void *的指針代表對象的地址冠句,而不是類型轻掩。 |
轉(zhuǎn)義字符
轉(zhuǎn)義序列 | 含義 |
---|---|
\\ | \字符 |
\' | '字符 |
\" | "字符 |
\? | ?字符 |
\a | 報警警報鈴聲 |
\b | 退格鍵 |
\f | 換頁符 |
\n | 換行符 |
\r | 回車 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八進制數(shù) |
\xhh... | 一個或多個數(shù)字的十六進制數(shù) |
3.2 C變量
C語言中每個變量都有特定的類型幸乒,類型決定了變量存儲的大小和布局。變量的名稱可以由字母唇牧、數(shù)字和下劃線字符組成罕扎。它必須以字母或下劃線開頭。變量基本類型與數(shù)據(jù)類型相對應(yīng)丐重。
變量可以是如下類型:char腔召、int、float扮惦、double臀蛛、void、枚舉崖蜜、指針浊仆、數(shù)組、結(jié)構(gòu)豫领、共用體等抡柿。
不帶初始化的變量定義:帶有靜態(tài)存儲持續(xù)時間的變量會被隱式初始化為NULL(所有字節(jié)的值都是0),其他所有變量的初始值是未定義的等恐。
左值(lvalue):指向內(nèi)存位置的表達式被稱為左值(lvalue)表達式洲劣;左值可以出現(xiàn)在賦值號的左邊或右邊。
右值(rvalue):指的是存儲在內(nèi)存中某些地址的數(shù)值课蔬;右值是不能對其進行賦值的表達式囱稽,也就是說右值只能出現(xiàn)在賦值號的右邊。
3.3 C常量
3.3.1 什么是常量二跋?
常量是固定值战惊,在定義之后就不能進行修改(程序執(zhí)行期間不會發(fā)生改變)。這種固定的值同欠,又叫做字面量样傍。常量就像常規(guī)的變量,只不過常量的值在定以后不能進行修改铺遂。
常量也可以是任意的基本數(shù)據(jù)類型衫哥,比如:整數(shù)常量、浮點常量襟锐、字符常量撤逢、字符串常量,或枚舉常量。
整數(shù)常量前面可以添加前綴:0x或0X表示十六進制蚊荣,0表示八進制初狰,不帶前綴表示默認十進制;也可以帶U或L后綴:U表示無符號整數(shù)互例,L表示長整數(shù)奢入。U或L可以大寫,也可以小寫媳叨,順序任意腥光。
浮點常量由整數(shù)部分、小數(shù)點糊秆、小數(shù)部分和指數(shù)部分組成武福。指數(shù)是用e或E來表示的。
字符常量是括在單引號''中的痘番,可以是普通字符(如'x')捉片、轉(zhuǎn)義字符(如‘\t’,鍵轉(zhuǎn)義字符表)汞舱,或一個通用字符(如‘\u02c0’)伍纫。
字符串常量是括在雙引號""中的, 可以是普通的字符兵拢、轉(zhuǎn)義序列或通用字符翻斟。可以用空格做分隔符说铃,表示一個很長的字符串常量访惜。
3.3.2 常量的定義
在C中,有兩種簡單的方式定義常量:
- 使用#define預(yù)處理器腻扇;
- 使用const關(guān)鍵字债热;
使用#define預(yù)處理器定義常量的形式為:
#define identifier value
也可以使用const前綴聲明指定類型的常量:
const type variable = value;
通常,我們使用大寫字母來表示常量幼苛,這是一個很好的編程習(xí)慣窒篱。
3.4 C存儲類
存儲類定義了C程序中變量/函數(shù)的可見性范圍和生命周期,這些存儲類說明符放置在所修飾的類型之前舶沿。常見的C存儲類說明符如下:
- auto
- register
- static
- extern
3.4.1 auto存儲類
auto存儲類是所有局部變量默認的存儲類墙杯,且auto只能用在函數(shù)內(nèi),即auto只能修飾局部變量括荡。
3.4.2 register存儲類
register 存儲類用于定義存儲在寄存器中而不是 RAM 中的局部變量高镐。這意味著變量的最大尺寸等于寄存器的大小(通常是一個詞)畸冲,且不能對它應(yīng)用一元的 '&' 運算符(因為它沒有內(nèi)存位置)嫉髓。
寄存器只用于需要快速訪問的變量观腊,比如計數(shù)器。還應(yīng)注意的是算行,定義 'register' 并不意味著變量將被存儲在寄存器中梧油,它意味著變量可能存儲在寄存器中,這取決于硬件和實現(xiàn)的限制州邢。
3.4.3 static存儲類
static 存儲類指示編譯器在程序的生命周期內(nèi)保持局部變量的存在儡陨,而不需要在每次它進入和離開作用域時進行創(chuàng)建和銷毀。因此偷霉,使用 static 修飾局部變量可以在函數(shù)調(diào)用之間保持局部變量的值迄委。
static 修飾符也可以應(yīng)用于全局變量褐筛。當(dāng) static 修飾全局變量時类少,會使變量的作用域限制在聲明它的文件內(nèi)。
在 C 編程中渔扎,當(dāng) static 用在類數(shù)據(jù)成員上時硫狞,會導(dǎo)致僅有一個該成員的副本被類的所有對象共享。
3.4.4 extern存儲類
extern 存儲類用于提供一個全局變量的引用晃痴,全局變量對所有的程序文件都是可見的残吩。當(dāng)您使用 'extern' 時,對于無法初始化的變量倘核,會把變量名指向一個之前定義過的存儲位置泣侮。
當(dāng)您有多個文件且定義了一個可以在其他文件中使用的全局變量或函數(shù)時,可以在其他文件中使用 extern 來得到已定義的變量或函數(shù)的引用紧唱』钭穑可以這么理解,extern 是用來在另一個文件中聲明一個全局變量或函數(shù)漏益。
extern 修飾符通常用于當(dāng)有兩個或多個文件共享相同的全局變量或函數(shù)的時候蛹锰。
四、C復(fù)合數(shù)據(jù)
4.1 數(shù)組
數(shù)組是可以存儲一個固定大小的相同類型元素的順序集合绰疤。所有的數(shù)組都是由連續(xù)的內(nèi)存位置組成铜犬。在C中聲明一個數(shù)組,需要指定元素的類型的元素的數(shù)量:
type arrayName[ arraySize ];
arraySize必須是一個大于等于0的整數(shù)常量轻庆,type可以是任意有效的C數(shù)據(jù)類型癣猾。
在C中,您可以逐個初始化數(shù)組余爆,也可以使用一個大括號{}的初始化語句纷宇。
多維數(shù)組
C支持多維數(shù)組,多維數(shù)組最簡單的形式是二維數(shù)組龙屉。
type name[size1][size2]...[sizeN];
傳遞數(shù)組給函數(shù)
傳遞數(shù)組作為函數(shù)的參數(shù)呐粘,可使用如下三種方式來實現(xiàn):
# 方式 1
void myFunction(int *param)
{
...
}
# 方式 2
void myFunction(int param[10])
{
...
}
# 方式 3
void myFunction(int param[])
{
...
}
函數(shù)返回數(shù)組
C語言不允許返回一個完整的數(shù)組满俗,但是可以通過不帶索引的數(shù)組名來返回一個指向數(shù)組的指針:
# 返回type類型的數(shù)組指針
type * myFunction()
{
...
}
注意:C不支持函數(shù)返回局部變量的地址,除非局部變量定義為static作岖。
指向數(shù)組的指針
數(shù)組名是一個指向數(shù)組中第一個元素的常量指針唆垃。
# array是一個指向array[0]的指針,值為&array[0]
type array[N];
# p是一個指向type數(shù)據(jù)類型的指針
type *p;
# 給p賦值痘儡,讓p指向array數(shù)組的第一個元素
p = array;
# 使用指針p來訪問數(shù)組:
*p;
*p++;
*(p+3);
4.2 指針
每一個變量都有一個內(nèi)存位置辕万,每一個內(nèi)存位置都定義了可使用連字號(&)運算符訪問其地址的方法,它表示了內(nèi)存中的一個地址沉删。
指針是一個變量渐尿,其值為另一個變量的地址,即內(nèi)存位置的直接地址矾瑰。
type *var-name;
所有指針的值的實際數(shù)據(jù)類型砖茸,不管是整型、浮點型殴穴、字符型凉夯,還是其他的數(shù)據(jù)類型,都是一樣的采幌,都是一個代表內(nèi)存地址的長的十六進制數(shù)劲够。不同數(shù)據(jù)類型的指針之間唯一的不同是,指針?biāo)赶虻淖兞炕虺A康臄?shù)據(jù)類型不同休傍。
NULL指針
在變量聲明的時候征绎,如果沒有確切的地址可以賦值,為指針變量賦一個 NULL 值是一個良好的編程習(xí)慣磨取。賦為 NULL 值的指針被稱為空指針人柿。NULL 指針是一個定義在標(biāo)準(zhǔn)庫中的值為零的常量。
在大多數(shù)的操作系統(tǒng)上寝衫,程序不允許訪問地址為 0 的內(nèi)存顷扩,因為該內(nèi)存是操作系統(tǒng)保留的。然而慰毅,內(nèi)存地址 0 有特別重要的意義隘截,它表明該指針不指向一個可訪問的內(nèi)存位置。但按照慣例汹胃,如果指針包含空值(零值)婶芭,則假定它不指向任何東西。
C指針?biāo)阈g(shù)運算
C指針是一個用數(shù)值表示的地址着饥,因此犀农,可以對指針執(zhí)行算術(shù)運算:++、--宰掉、+呵哨、-赁濒、==、<孟害、>拒炎。
C指針數(shù)組
聲明ptr為一個數(shù)組,每一個ptr數(shù)組的元素都是一個指向int類型值的指針挨务。
int *ptr[MAX];
C數(shù)組指針
聲明ptr為一個指針击你,指向一個元素個數(shù)為MAX個int類型的數(shù)組的指針。
int (*ptr)[MAX];
C指向指針的指針
指向指針的指針是一種兩級間接尋址的形式谎柄,是一個指針鏈丁侄。當(dāng)然也可以定義多級間接尋址的形式。定義指向指針的指針:
int var;
int *ptr1 = &var;
int **ptr2 = &ptr1;
C傳遞指針給函數(shù)
當(dāng)通過指針的方式傳遞給函數(shù)時朝巫,我們可以在函數(shù)內(nèi)部修改外部變量的值鸿摇。
C從函數(shù)返回指針
C語言可以通過返回指針的方式來返回多個值,返回靜態(tài)變量捍歪,返回動態(tài)分配的內(nèi)存户辱。但是要注意:C不支持在函數(shù)外返回局部變量的地址,除非定義局部變量為static變量糙臼。
4.3 字符串
在C語言中,字符串實際上是使用null字符('\0')終止的一維字符數(shù)組恩商。C中有大量操作字符串的函數(shù):
序號 | 函數(shù) & 目的 |
---|---|
1 | strcpy(s1, s2); 復(fù)制字符串s2到字符串s1 |
2 | strcat(s1, s2); 連接字符串s2到字符串s1的末尾 |
3 | strlen(s1); 返回字符串s1的長度 |
4 | strcmp(s1, s2); 如果s1和s2是相同的变逃,則返回0;如果s1 < s2則返回小于0怠堪;如果s1 > s2則返回大于0 |
5 | strchr(s1, ch); 返回一個指針揽乱,指向字符串s1中字符ch的第一次出現(xiàn)的位置。 |
6 | strstr(s1, s2); 返回一個指針粟矿,指向字符串s1中字符串s2的第一次出現(xiàn)的位置。 |
4.4 結(jié)構(gòu)體
C數(shù)組允許定義可存儲相同類型數(shù)據(jù)的變量;結(jié)構(gòu)是C中另一種用戶自定義的可用數(shù)據(jù)類型呵萨,它可以存儲不同類型的數(shù)據(jù)項某宪。
結(jié)構(gòu)體定義
struct [struct tag]
{
member definition;
member definition;
...
member definition;
}[one or more structure variable];
struct tag是可選的,每個member definition是標(biāo)準(zhǔn)的變量定義掏秩;在結(jié)構(gòu)體定義的末尾或舞,最后一個分號之前,可以指定一個或多個結(jié)構(gòu)體變量蒙幻,也是可選的映凳。
訪問結(jié)構(gòu)成員
為了訪問結(jié)構(gòu)的成員,我們使用成員訪問運算符(.)邮破,如果是指針的話诈豌,則使用(->)仆救。
結(jié)構(gòu)作為參數(shù)
可以把結(jié)構(gòu)體作為參數(shù),傳參方式與其他基本類型的變量或指針類似矫渔,都是值傳遞方式派桩。
4.5 共用體
共用體是一種特殊的數(shù)據(jù)結(jié)構(gòu),允許你在相同的內(nèi)存位置存儲不同的數(shù)據(jù)類型蚌斩。你可以定義一個帶有多成員的共用體铆惑,但是任何時候只能有一個成員帶值。
union [union tag]
{
member definition;
member definition;
...
member definition;
}[one or more union variable];
union tag是可選的送膳,每個member definition是標(biāo)準(zhǔn)的變量定義员魏;在共用體定義的末尾,最后一個分號之前叠聋,你可以定義一個或多個共用體變量撕阎,也是可選的。
共用體的訪問方式與結(jié)構(gòu)體一樣碌补,只是要注意任何時候只能有一個成員帶值
4.6 位域
有些信息在存儲時虏束,并不需要占用一個完整字節(jié),而只需要占幾個或一個二進制位厦章。為了節(jié)省存儲空間镇匀,并使處理簡便,C語言提供了一種數(shù)據(jù)結(jié)構(gòu)袜啃,稱為“位域”或“位段”汗侵。
所謂“位域”就是把一個字節(jié)中的二進制位劃分為幾個不同的區(qū)域,并說明每個區(qū)域的位數(shù)群发。每個域有一個域名晰韵,允許在程序中按域名進行操作。這樣就可以把幾個不同的對象用一個字節(jié)的二進制位域來表示熟妓。
struct 位域結(jié)構(gòu)名
{
類型說明符 位域名1:位域長度;
類型說明符 位域名2:位域長度;
...
類型說明符 位域名n:位域長度;
}
位域的使用與結(jié)構(gòu)體和共用體類似雪猪。但應(yīng)注意以下幾點:
- 一個位域必須存儲在同一個字節(jié)中,不能跨兩個字節(jié)起愈。如一個字節(jié)所手缓蓿空間不夠存放另一個位域時,應(yīng)從下一個單元存放該位域告材;也可以有意使某位域從下一單元開始坤次,比如:
struct bs{
unsigned a:4;
unsigned :4;
unsigned b:4;
unsigned c:4;
}
- 由于位域不允許跨兩個字節(jié),因此位域的長度不能大于一個字節(jié)的長度斥赋,也就是說不能超過8位二進位缰猴。如果最大長度大于計算機的整數(shù)字長,一些編譯器可能會允許域的內(nèi)存重疊疤剑,另外一些編譯器可能會把大于一個域的部分存儲在下一個字中滑绒。
- 位域可以是無名位域闷堡,這時它只用來作填充或調(diào)整位置。無名的位域是不能使用的疑故。
4.7 typedef
typedef關(guān)鍵字可以用來為類型取一個新名字杠览。按照慣例,定義新名字時會使用大寫字母纵势,以便提醒用戶類型名稱是一個象征性的縮寫踱阿。如下:
typedef unsigned char BYTE
你也可以使用typedef來為用戶自定義的數(shù)據(jù)類型(結(jié)構(gòu)體、枚舉钦铁、共用體软舌、位域結(jié)構(gòu))取一個新名字。
typedef與define
- typedef僅限于類型定義符號名稱牛曹,#define不僅可以為類型定義別名佛点,也能為數(shù)值定義別名(1定義為ONE);
- typedef是由編譯器執(zhí)行解釋的,#define語句是預(yù)編譯器進行擴展處理的黎比。
4.8 強制類型轉(zhuǎn)換
強制類型轉(zhuǎn)換是把變量從一種類型轉(zhuǎn)換為另一種數(shù)據(jù)類型超营。使用強制類型轉(zhuǎn)換運算符顯示的轉(zhuǎn)換一種類型:
(type_name) expression
類型的轉(zhuǎn)換經(jīng)常是隱式的,由編譯器自動執(zhí)行的阅虫。但是演闭,作為一個有追求的程序員,建議都是用顯示的類型轉(zhuǎn)換书妻,這也是一個良好的編程習(xí)慣船响。
整數(shù)提升:整數(shù)提升是指把小于int或unsigned int的整數(shù)類型轉(zhuǎn)換為int或unsigned int的過程。
常用的算是轉(zhuǎn)換不適用于賦值運算符躲履、邏輯運算符&&和||。比如:
int i = 17;
char c = 'c';
float sum = i + c;
由于最后的值是float類型聊闯,所以編譯器會先把i和c轉(zhuǎn)換為浮點型工猜,然后將其相加后賦值給sum。
五菱蔬、C作用域規(guī)則
任何一種編程中篷帅,作用域是程序中定義的變量所存在的區(qū)域,超過該區(qū)域變量就不能被訪問拴泌。C 語言中有三個地方可以聲明變量:
- 在函數(shù)或塊內(nèi)部的局部變量
- 在所有函數(shù)外部的全局變量
- 在形式參數(shù)的函數(shù)參數(shù)定義中
5.1 局部變量
在某個函數(shù)或塊的內(nèi)部聲明的變量稱為局部變量魏身。它們只能被該函數(shù)或該代碼塊內(nèi)部的語句使用。局部變量在函數(shù)外部是不可知的蚪腐。
5.2 全局變量
全局變量是定義在函數(shù)外部箭昵,通常是在程序的頂部。全局變量在整個程序生命周期內(nèi)都是有效的回季,在任意的函數(shù)內(nèi)部能訪問全局變量家制。全局變量可以被任何函數(shù)訪問正林。也就是說,全局變量在聲明后整個程序中都是可用的颤殴。
在程序中觅廓,局部變量和全局變量的名稱可以相同,但是在函數(shù)內(nèi)涵但,局部變量的值會覆蓋全局變量的值杈绸。
5.3 形式參數(shù)
函數(shù)的參數(shù),形式參數(shù)矮瘟,被當(dāng)作該函數(shù)內(nèi)的局部變量瞳脓,它們會優(yōu)先覆蓋全局變量。
5.3 初始化局部變量和全局變量
當(dāng)局部變量被定義時芥永,系統(tǒng)不會對其初始化篡殷,您必須自行對其初始化。定義全局變量時埋涧,系統(tǒng)會自動對其初始化板辽。
數(shù)據(jù)類型 | 初始化默認值 |
---|---|
int | 0 |
char | '' |
float | 0 |
double | 0 |
pointer | NULL |
正確地初始化變量是一個良好的編程習(xí)慣,否則有時候程序可能會產(chǎn)生意想不到的結(jié)果棘催,因為未初始化的變量會導(dǎo)致一些在內(nèi)存位置中已經(jīng)可用的垃圾值劲弦。
注意:變量的作用域可以通過前面講解的static和extern關(guān)鍵字修改。
六醇坝、C運算符
運算符是一種告訴編譯器執(zhí)行特定的數(shù)學(xué)或邏輯操作的符號邑跪。C語言提供了如下類型的運算符:
- 算符運算符
- 關(guān)系運算符
- 邏輯運算符
- 位運算符
- 賦值運算符
- 雜項運算符
6.1 算術(shù)運算符
假設(shè)A為10,B為20呼猪。
運算符 | 描述 | 實例 |
---|---|---|
+ | 把兩個操作數(shù)相加 | A + B將得到30 |
- | 從第一個操作數(shù)中減去第二個操作數(shù) | A - B將得到-10 |
* | 把兩個操作數(shù)相乘 | A * B將得到200 |
/ | 分子除以分母 | B / A將得到2 |
% | 取模運算符画畅,整除后的余數(shù) | B % A將得到0 |
++ | 自增運算符,整數(shù)值增加1 | A++將得到11 |
-- | 自減運算符宋距,整數(shù)值減少1 | A--將得到9 |
a++與++a:a++是先操作后再+1轴踱,++a是先+1后再操作。
6.2 關(guān)系運算符
假設(shè)A值為10谚赎,B值為20淫僻。
運算符 | 描述 | 實例 |
---|---|---|
== | 檢查兩個操作數(shù)的值是否相等,如果相等則條件為真 | (A == B)為假 |
!= | 檢查兩個操作數(shù)的值是否相等壶唤,如果不相等則條件為真 | (A != B)為真 |
> | 檢查左操作數(shù)的值是否大于右操作數(shù)的值雳灵,如果是則條件為真 | (A > B)為假 |
< | 檢查左操作數(shù)的值是否小于右操作數(shù)的值,如果是則條件為真 | (A < B)為真 |
>= | 檢查左操作數(shù)的值是否大于或等于右操作數(shù)的值闸盔,如果是則條件為真 | (A >= B)為假 |
<= | 檢查左操作數(shù)的值是否小于或等于右操作數(shù)的值悯辙,如果是則條件為真 | (A <= B)為真 |
6.3 邏輯運算符
假設(shè)A的值為1,B的值為0。
運算符 | 描述 | 實例 |
---|---|---|
&& | 邏輯與運算符:如果兩個操作數(shù)都非0笑撞,則條件為真 | (A && B)為假 |
| | 邏輯或運算符:如果兩個操作數(shù)中有任意一個非0岛啸,則條件為真 | (A || B)為真 |
! | 邏輯非運算符:用來逆轉(zhuǎn)操作數(shù)的邏輯狀態(tài),如果條件為真則邏輯非運算符將使其為假 | !( A && B)為真 |
6.4 位運算符
假設(shè)A值為60茴肥,B值為13坚踩,則:
運算符 | 描述 | 實例 |
---|---|---|
& | 按位與操作,按二進制位進行‘與’運算瓤狐。運算規(guī)則見下面真值表 | (A & B)將得到12 |
按位或運算符瞬铸,按二進制位進行‘或’運算。運算規(guī)則見下面真值表 | (A | B)將得到61 | |
^ | 按位異或運算符础锐,按二進制位進行‘異或’運算嗓节。運算規(guī)則見下面真值表 | (A ^ B)將得到48 |
~ | 按位取反運算符,按二進制位進行‘取反’運算 | (~A)將得到帶符號的-61 |
<< | 二進制左移運算符皆警,左操作數(shù)的值向左移動右操作數(shù)指定的位數(shù)(左邊的二進制位丟棄拦宣,右邊補0) | A << 2將得到240 |
>> | 二進制右移運算符,左操作數(shù)的值向右移動右操作數(shù)指定的位數(shù)(正數(shù)左補0信姓,負數(shù)左補1鸵隧,右邊位丟棄) | A >> 2將得到15 |
&、|和^位操作真值表如下:
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
6.5 賦值運算符
運算符 | 描述 | 實例 |
---|---|---|
= | 簡單的賦值運算符意推,把右邊操作數(shù)的值賦給左邊操作數(shù) | C = A + B將把A + B的值賦給C |
+= | 加且賦值運算符豆瘫,把左邊操作數(shù)加上右邊操作數(shù)的結(jié)果賦值給左邊操作數(shù) | C += A相當(dāng)于C = C + A |
-= | 減且賦值運算符,把左邊操作數(shù)減去右邊操作數(shù)的結(jié)果賦值給左邊操作數(shù) | C -= A相當(dāng)于C = C - A |
*= | 乘且賦值運算符菊值,把左邊操作數(shù)除以右邊操作數(shù)的結(jié)果賦值給左邊操作數(shù) | C /= A相當(dāng)于C = C*A |
/= | 除且賦值運算符外驱,把左邊操作數(shù)除以右邊操作數(shù)的結(jié)果賦值給左邊操作數(shù) | C /= A相當(dāng)于C = C/A |
%= | 求模且賦值運算符,把左邊操作數(shù)對右邊操作數(shù)取模的結(jié)果賦值給左邊操作數(shù) | C %= A相當(dāng)于C = C%A |
<<= | 左移且賦值運算符 | C <<= 2等同于C = C<<2 |
>>= | 右移且賦值運算符 | C >>= 2等同于C = C>>2 |
&= | 按位與且賦值運算符 | C &= 2等同于C = C&2 |
= | 按位或且賦值運算符 | C |= 2等同于C = C | 2 |
^= | 按位異或且賦值運算符 | C ^= 2等同于C = C^2 |
6.6 雜項運算符
運算符 | 描述 | 實例 |
---|---|---|
sizeof() | 返回變量占用內(nèi)存的大小 | sizeof(a)將返回變量a占用的內(nèi)存大小 |
& | 返回變量的地址 | &a將給出變量的實際地址 |
* | 指向一個變量 | *a將指向一個變量 |
?: | 條件表達式 | 如果條件為真腻窒?則值為X : 否則值為Y |
6.7 運算符優(yōu)先級
運算符的優(yōu)先級確定表達式中項的組合優(yōu)先順序昵宇,這會影響到一個表達式如何計算。較高優(yōu)先級的運算符會被優(yōu)先計算儿子。
類別 | 運算符 | 結(jié)合性 |
---|---|---|
后綴 | () [] -> . ++ -- | 從左到右 |
一元 | + - ! ~ ++ -- (type) * & sizeof | 從右到左 |
乘除 | * / % | 從左到右 |
加減 | + - | 從左到右 |
移位 | << >> | 從左到右 |
關(guān)系 | < <= > >= | 從左到右 |
相等 | == != | 從左到右 |
位與AND | & | 從左到右 |
位異或XOR | ^ | 從左到右 |
位或OR | | | 從左到右 |
邏輯與AND | && | 從左到右 |
邏輯或OR | || | 從左到右 |
條件 | 趟薄?: | 從右到左 |
賦值 | = += -= *= /= %= >>= <<= &= ^= |= | 從右到左 |
逗號 | , | 從左到右 |
七、C程序控制
7.1 C判斷
C語言把任何非零和非空的值假定為true典徊,把零或null假定為false。
7.1.1 if語句
一個if語句由一個布爾表達式后跟一個或多個語句組成恩够。
if(boolean_expression)
{
/* 如果布爾表達式為真將執(zhí)行的語句 */
}
7.1.2 if...else語句
一個if語句后可跟一個可選的else語句卒落,else語句在布爾表達式為假時執(zhí)行。
if(boolean_expression)
{
/* 如果布爾表達式為真將執(zhí)行的語句 */
}
else
{
/* 如果布爾表達式為假將執(zhí)行的語句 */
}
注意:條件運算符 ? : 可以用來替代if...else語句蜂桶。
7.1.3 if...else if...else
一個if語句后可跟一個可選的else if ... else語句儡毕,這可用于測試多種條件。
if(boolean_expression 1)
{
/* 當(dāng)布爾表達式1為真時執(zhí)行 */
}
else if(boolean_expression 2)
{
/* 當(dāng)布爾表達式2為真時執(zhí)行 */
}
else if(boolean_expression 3)
{
/* 當(dāng)布爾表達式3為真時執(zhí)行 */
}
else
{
/* 當(dāng)上面條件都不為真時執(zhí)行 */
}
7.1.4 嵌套if語句
可以在一個if或else if語句內(nèi)使用另一個if或else if語句。
if(boolean_expression 1)
{
/* 當(dāng)布爾表達式1為真時執(zhí)行 */
if(boolean_expression 2)
{
/* 當(dāng)布爾表達式2為真時執(zhí)行 */
}
else
{
/* 當(dāng)布爾表達式2為假時執(zhí)行 */
}
}
else
{
/* 當(dāng)布爾表達式1為假時執(zhí)行 */
}
7.1.5 switch語句
一個switch語句允許測試一個變量等于多個值時的情況腰湾。每個值稱為一個case雷恃,且被測試的變量會對每個switch case進行檢查。
switch(expression){
case constant-expression :
statement(s);
break; /* 可選的 */
case constant-expression :
statement(s);
break; /* 可選的 */
/* 你可以有任意數(shù)量的case語句 */
default : /* 可選的 */
statement(s);
break; /* 可選的 */
}
switch語句遵循如下規(guī)則:
- switch 語句中的 expression 必須是一個整型或枚舉類型费坊,或者是一個 class 類型倒槐,其中 class 有一個單一的轉(zhuǎn)換函數(shù)將其轉(zhuǎn)換為整型或枚舉類型。
- 在一個 switch 中可以有任意數(shù)量的 case 語句附井。每個 case 后跟一個要比較的值和一個冒號讨越。
- case 的 constant-expression 必須與 switch 中的變量具有相同的數(shù)據(jù)類型,且必須是一個常量或字面量永毅。
- 當(dāng)被測試的變量等于 case 中的常量時把跨,case 后跟的語句將被執(zhí)行,直到遇到 break 語句為止沼死。
- 當(dāng)遇到 break 語句時着逐,switch 終止,控制流將跳轉(zhuǎn)到 switch 語句后的下一行意蛀。
- 不是每一個 case 都需要包含 break耸别。如果 case 語句不包含 break,控制流將會 繼續(xù) 后續(xù)的 case浸间,直到遇到 break 為止太雨。
- 一個 switch 語句可以有一個可選的 default case,出現(xiàn)在 switch 的結(jié)尾魁蒜。default case 可用于在上面所有 case 都不為真時執(zhí)行一個任務(wù)囊扳。default case 中的 break 語句不是必需的。
7.1.6 嵌套switch語句
可以把一個 switch 作為一個外部 switch 的語句序列的一部分兜看,即可以在一個 switch 語句內(nèi)使用另一個 switch 語句锥咸。即使內(nèi)部和外部 switch 的 case 常量包含共同的值,也沒有矛盾细移。
switch(ch1) {
case 'A':
printf("這個 A 是外部 switch 的一部分" );
switch(ch2) {
case 'A':
printf("這個 A 是內(nèi)部 switch 的一部分" );
break;
case 'B': /* 內(nèi)部 B case 代碼 */
}
break;
case 'B': /* 外部 B case 代碼 */
}
當(dāng)然搏予,if語句與switch之間也是可以互相嵌套使用的,只是咱們編程的時候注意嵌套層數(shù)最好不好超過3層弧轧,否則不利于代碼的理解雪侥。
7.2 C循環(huán)
7.2.1 while循環(huán)
當(dāng)給定條件為真時,重復(fù)語句或語句組精绎,它會在執(zhí)行循環(huán)主體之前測試條件
while(condition)
{
statement(s);
}
7.2.2 for循環(huán)
多次執(zhí)行一個語句序列速缨,簡化管理循環(huán)變量的代碼。
for (init; condition; increment)
{
statement(s);
}
下面是for循環(huán)的控制流程:
- init 會首先被執(zhí)行代乃,且只會執(zhí)行一次旬牲。這一步允許您聲明并初始化任何循環(huán)控制變量。您也可以不在這里寫任何語句,只要有一個分號出現(xiàn)即可原茅。
- 接下來吭历,會判斷 condition。如果為真擂橘,則執(zhí)行循環(huán)主體晌区。如果為假,則不執(zhí)行循環(huán)主體贝室,且控制流會跳轉(zhuǎn)到緊接著 for 循環(huán)的下一條語句契讲。
- 在執(zhí)行完 for 循環(huán)主體后,控制流會跳回上面的 increment 語句滑频。該語句允許您更新循環(huán)控制變量捡偏。該語句可以留空,只要在條件后有一個分號出現(xiàn)即可峡迷。
- 條件再次被判斷银伟。如果為真,則執(zhí)行循環(huán)绘搞,這個過程會不斷重復(fù)(循環(huán)主體彤避,然后增加步值,再然后重新判斷條件)夯辖。在條件變?yōu)榧贂r琉预,for 循環(huán)終止。
7.2.3 do...while循環(huán)
除了它是在循環(huán)主體結(jié)尾測試條件外蒿褂;其他與while語句類似圆米,但do...while循環(huán)至少執(zhí)行一次循環(huán)。
do
{
statement(s);
}while(condition);
7.2.4 嵌套循環(huán)
可以在while啄栓、for或do...while循環(huán)內(nèi)使用一個或多個循環(huán)娄帖。
// for循環(huán)嵌套
for (init; condition; increment)
{
for(init; condition; increment)
{
statement(s);
}
statements(s);
}
// while循環(huán)嵌套
while(condition)
{
while(condition)
{
statement(s);
}
statement(s);
}
// do...while循環(huán)嵌套
do
{
statement(s);
do
{
statement(s);
}while(condition);
}while(condition);
7.2.5 循環(huán)控制語句
break語句:終止loop或switch語句,程序流將繼續(xù)執(zhí)行緊接著loop或switch的下一條語句昙楚;若是嵌套循環(huán)近速,break語句會停止最內(nèi)層的循環(huán)。
continue語句:引起循環(huán)跳過主體的剩余部分堪旧,立即重新開始測試條件削葱。
goto語句:將控制轉(zhuǎn)移到被標(biāo)記的語句,但不建議在程序中使用goto語句淳梦。
注意:在任何編程語言中蟹但,都不建議使用 goto 語句崎脉。因為它使得程序的控制流難以跟蹤娜遵,使程序難以理解和難以修改乎澄。任何使用 goto 語句的程序可以改寫成不需要使用 goto 語句的寫法。
7.2.6 無限循環(huán)
如果條件永遠為真螃宙,則循環(huán)將變成無限循環(huán)蛮瞄。一般情況下,使用for(;;)結(jié)構(gòu)來表示一個無限循環(huán)谆扎。
可以按Ctrl + C鍵終止一個無限循環(huán)挂捅。
八、C函數(shù)
8.1 函數(shù)定義
C語言定義函數(shù)的一般形式為:
return_type function_name(parameter_list)
{
body of the functioin
}
在 C 語言中堂湖,函數(shù)由一個函數(shù)頭和一個函數(shù)主體組成闲先。下面列出一個函數(shù)的所有組成部分:
- 返回類型:一個函數(shù)可以返回一個值。return_type 是函數(shù)返回的值的數(shù)據(jù)類型无蜂。有些函數(shù)執(zhí)行所需的操作而不返回值伺糠,在這種情況下,return_type 是關(guān)鍵字 void斥季。
- 函數(shù)名稱:這是函數(shù)的實際名稱训桶。函數(shù)名和參數(shù)列表一起構(gòu)成了函數(shù)簽名。
- 參數(shù):參數(shù)就像是占位符酣倾。當(dāng)函數(shù)被調(diào)用時舵揭,您向參數(shù)傳遞一個值,這個值被稱為實際參數(shù)躁锡。參數(shù)列表包括函數(shù)參數(shù)的類型午绳、順序、數(shù)量映之。參數(shù)是可選的拦焚,也就是說,函數(shù)可能不包含參數(shù)惕医。
- 函數(shù)主體:函數(shù)主體包含一組定義函數(shù)執(zhí)行任務(wù)的語句耕漱。
8.2 函數(shù)聲明
函數(shù)聲明會告訴編譯器函數(shù)名稱及如何調(diào)用函數(shù)。函數(shù)的實際主體可以單獨定義抬伺。函數(shù)聲明包括以下幾個部分:
return_type function_name(parameter_list)
8.3 函數(shù)調(diào)用
當(dāng)程序調(diào)用函數(shù)時螟够,程序控制權(quán)會轉(zhuǎn)移給被調(diào)用的函數(shù)。被調(diào)用的函數(shù)執(zhí)行已定義的任務(wù)峡钓,當(dāng)函數(shù)的返回語句被執(zhí)行時妓笙,或到達函數(shù)的結(jié)束括號時,會把程序控制權(quán)交還給主程序能岩。
8.3 函數(shù)參數(shù)
如果函數(shù)要使用參數(shù)寞宫,則必須聲明接受參數(shù)值的變量。這些變量稱為函數(shù)的形式參數(shù)拉鹃。
形式參數(shù)就像函數(shù)內(nèi)的其他局部變量辈赋,在進入函數(shù)時被創(chuàng)建鲫忍,退出函數(shù)時被銷毀。當(dāng)調(diào)用函數(shù)時钥屈,有兩種向函數(shù)傳遞參數(shù)的方式:
調(diào)用類型 | 描述 |
---|---|
傳值調(diào)用 | 該方法把參數(shù)的實際值復(fù)制給函數(shù)的形式參數(shù)悟民。在這種情況下,修改函數(shù)內(nèi)的形式參數(shù)不會影響實際參數(shù)篷就。 |
引用調(diào)用 | 該方法把參數(shù)的地址復(fù)制給形式參數(shù)射亏。在函數(shù)內(nèi),該地址用于訪問調(diào)用中要用到的實際參數(shù)竭业。這意味著智润,修改形式參數(shù)會影響實際參數(shù)。 |
默認情況下未辆,C使用傳值調(diào)用來傳遞參數(shù)(函數(shù)內(nèi)的代碼不能改變用于調(diào)用函數(shù)的實際參數(shù))窟绷。
8.4 遞歸
遞歸是以自相似的方式重復(fù)項目的處理過程。同樣地鼎姐,在編程語言中钾麸,在函數(shù)內(nèi)部調(diào)用函數(shù)自身,稱為遞歸調(diào)用炕桨。
在使用遞歸時饭尝,程序員需要注意定義一個從函數(shù)退出的條件,否則會進入無限循環(huán)献宫。遞歸函數(shù)在解決許多數(shù)學(xué)問題上起了至關(guān)重要的作用钥平,比如計算一個數(shù)的階乘、生成斐波那契數(shù)列姊途,等等涉瘾。
數(shù)的階乘:
int factorial(unsigned int i)
{
if(i <= 1)
return 1;
return i*factorial(i - 1);
}
斐波那契數(shù)列:
int fibonaci(int i)
{
if(i == 0)
return 0;
if(i == 1)
return 1;
return fibonaci(i - 1) + fibonaci(i - 2);
}
九、C文件讀寫
一個文件捷兰,無論它是文本文件還是二進制文件立叛,都是代表了一系列的字節(jié)。C語言不僅提供了訪問頂層的函數(shù)贡茅,也提供了底層(OS)調(diào)用來處理存儲設(shè)備上的文件秘蛇。
9.1 打開文件
fopen()函數(shù)來創(chuàng)建一個新的文件或者打開一個已有的文件,這個調(diào)用會初始化類型 FILE 的一個對象顶考,類型 FILE 包含了所有用來控制流的必要的信息:
FILE *fopen(const char *filename, const char *mode);
filename是字符串赁还,用來命名文件,訪問模式mode的值可以是下列值的一個:
模式 | 描述 |
---|---|
r | 打開一個已有的文本文件驹沿,允許讀取文件 |
w | 打開一個文本文件艘策,允許寫入文件。如果文件不存在渊季,則會創(chuàng)建一個新文件朋蔫。在這里罚渐,您的程序會從文件的開頭寫入內(nèi)容 |
a | 打開一個文本文件,以追加模式寫入文件斑举。如果文件不存在搅轿,則會創(chuàng)建一個新文件。在這里富玷,您的程序會在已有的文件內(nèi)容中追加內(nèi)容 |
r+ | 打開一個文本文件,允許讀寫文件 |
w+ | 打開一個文本文件既穆,允許讀寫文件赎懦。如果文件已存在,則文件會被截斷為零長度幻工,如果文件不存在励两,則會創(chuàng)建一個新文件 |
a+ | 打開一個文本文件,允許讀寫文件囊颅。如果文件不存在当悔,則會創(chuàng)建一個新文件。讀取會從文件的開頭開始踢代,寫入則只能是追加模式 |
9.2 關(guān)閉文件
當(dāng)使用完文件時盲憎,需要調(diào)用fclose()關(guān)閉對應(yīng)的文件,否則會造成資源浪費:
int fclose(FILE *fp);
如果成功關(guān)閉文件胳挎,fclose()函數(shù)返回零饼疙,如果關(guān)閉文件時發(fā)生錯誤,函數(shù)返回EOF慕爬。這個函數(shù)實際上窑眯,會清空緩沖區(qū)中的數(shù)據(jù),關(guān)閉文件医窿,并釋放用于該文件的所有內(nèi)存磅甩。
9.3 寫入文件
int fputc(int c, FILE *fp);
int fputs(const char *s, FILE *fp);
int fprintf(FILE *fp, const char *format, ...);
fputc()把參數(shù)c的字符值寫入到fp所指向的輸出流中,寫入成功返回寫入的字符姥卢,發(fā)生錯誤則返回EOF卷要;使用fputs()可以把以null結(jié)尾字符串s寫入fp指向的輸出流中,寫入成功返回一個非負值隔显,發(fā)生錯誤則返回EOF却妨;也可以使用fprintf()函數(shù)來格式化輸出字符串到fp指向的輸出流中。
9.4 讀取文件
int fgetc(FILE *fp);
char *fgets(char *buf, int n, FILE *fp);
int fscanf(FILE *fp, const char *format, ...);
fgetc()函數(shù)從fp所指向的輸入文本中讀取一個字符括眠,返回讀取的字符彪标,錯誤則返回EOF;fgets()從fp所指向的輸入流中讀取n-1個字符掷豺,將讀取到的字符串復(fù)制到緩沖區(qū)buf捞烟,并在最后追加一個null字符來終止字符串薄声;也可使用fscanf()函數(shù)從文件格式化讀取要獲取的值。
9.5 二進制I/O函數(shù)
下面兩個函數(shù)用于存儲塊的讀寫题画,常用于數(shù)組或結(jié)構(gòu)體:
size_t fread(void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
size_t fwrite(const void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
十默辨、C其他相關(guān)
10.1 C預(yù)處理器
C預(yù)處理不是編譯器的組成部分,但它是編譯過程中的一個步驟苍息。
所有的預(yù)處理器命令都是以井號(#)開頭缩幸,它必須是第一個非空字符,為了增加可讀性竞思,預(yù)處理器指令應(yīng)從第一列開始表谊。常見的預(yù)處理器指令如下:
指令 | 描述 |
---|---|
#define | 宏定義 |
#include | 包含一個源代碼文件 |
#undef | 取消已定義的宏 |
#ifdef | 如果宏已經(jīng)定義,則返回真 |
#ifndef | 如果宏沒有定義盖喷,則返回真 |
#if | 如果給定條件為真爆办,則編譯下面代碼 |
#else | #if的替代方案 |
#elif | 如果前面的#if給定條件不為真,elif后面為真則編譯下面的代碼 |
#endif | 結(jié)束一個#if ... #else條件編譯塊 |
#error | 當(dāng)遇到標(biāo)準(zhǔn)錯誤時课梳,輸出錯誤信息 |
#pragma | 使用標(biāo)準(zhǔn)化方法距辆,向編譯器發(fā)布特殊的命令到編譯器中 |
ANSI C定義了許多宏,在編譯中你可以使用這些宏暮刃,但不能直接修改這些預(yù)定義的宏:
宏 | 描述 |
---|---|
_DATE_ | 當(dāng)前日期跨算,一個以“MM DD YYYY”格式表示的字符常量 |
_TIME_ | 當(dāng)前時間,一個以“HH:MM:SS”格式表示的字符常量 |
_FILE_ | 這會包含當(dāng)前文件名沾歪,一個字符串常量 |
_LINE_ | 這會包含當(dāng)前行號漂彤,一個十進制常量 |
_STDC_ | 當(dāng)編譯器以ANSI標(biāo)準(zhǔn)編譯時,則定義為1 |
一個宏通常寫在一個單行上灾搏,但如果宏太長挫望,單行容不下則可以使宏延續(xù)運算符(\)。
在宏定義中狂窑,當(dāng)需要把一個宏的參數(shù)轉(zhuǎn)換為字符串常量時媳板,可以使用字符串常量化運算符(#)。
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
message_for(Carole, Debra)的結(jié)果是Carole and Debra: We love you!
宏定義內(nèi)的標(biāo)記粘貼運算符(##)會合并兩個參數(shù)泉哈,它允許在宏定義中兩個獨立的標(biāo)記被合并為一個標(biāo)記蛉幸。
#define tokenpaster(n) printf("token" #n " = %d\n", token##n)
int token32 = 40;
tokenpaster(32);
以上結(jié)果會打印:token32 = 40
預(yù)處理器defined運算符用在常量表達式中丛晦,用來確定一個標(biāo)識符是否已經(jīng)使用#define定義過奕纫,如果已定義則為真(非零),否則為假(零)烫沙。
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
10.2 C頭文件
頭文件是擴展名為.h的文件匹层,包含了C函數(shù)聲明和宏定義,被多個源文件中引用共享锌蓄。有兩種類型的頭文件:程序員編寫的頭文件和編譯器自帶的頭文件升筏。
A simple practice in C或C++程序中撑柔,建議把所有的常量、宏您访、系統(tǒng)全局變量和函數(shù)原型寫在頭文件中铅忿,在需要的時候隨時引用這個頭文件。
如果一個頭文件被引用兩次灵汪,編譯器會處理兩次頭文件的內(nèi)容檀训,這將產(chǎn)生錯誤。為了防止這種情況享言,標(biāo)準(zhǔn)的做法是把文件的整個內(nèi)容放在條件編譯語句中:
#ifndef HEAD_FILE
#define HAED_FILE
the entire header file file
#endif // HEAD_FILE
如上這種結(jié)構(gòu)就是常說的包裝器#ifndef肢扯。有時候需要從多個不同的頭文件中根據(jù)條件選擇一個引用到程序中,則可以通過一系列宏條件來實現(xiàn):
#if SYSTEM_1
#include "system_1.h"
#elif SYSTEM_2
#include "system_2.h"
#elif SYSTEM_3
...
#endif
10.3 C錯誤處理
C 語言不提供對錯誤處理的直接支持担锤,但是作為一種系統(tǒng)編程語言,它以返回值的形式允許您訪問底層數(shù)據(jù)乍钻。在發(fā)生錯誤時肛循,大多數(shù)的C或UNIX函數(shù)調(diào)用返回1或NULL,同時會設(shè)置一個錯誤代碼errno银择,該錯誤代碼是全局變量多糠,表示在函數(shù)調(diào)用期間發(fā)生了錯誤。您可以在 <error.h>頭文件中找到各種各樣的錯誤代碼浩考。
所以夹孔,C程序員可以通過檢查返回值,然后根據(jù)返回值決定采取哪種適當(dāng)?shù)膭幼魑瞿酢i_發(fā)人員應(yīng)該在程序初始化時搭伤,把errno設(shè)置為0,這是一種良好的編程習(xí)慣袜瞬。0值表示程序中沒有錯誤怜俐。
C語言提供了perror()和strerror()函數(shù)來顯示與errno相關(guān)的文本消息:
- perror()函數(shù)顯示您傳給它的字符串,后跟一個冒號邓尤、一個空格和當(dāng)前errno值的文本表示形式拍鲤。
- strerror()函數(shù)返回一個指針,指針指向當(dāng)前error值的文本表示形式汞扎。
10.5 C可變參數(shù)
有時候可能會碰到希望函數(shù)帶有可變數(shù)量的參數(shù)季稳,而不是預(yù)定義數(shù)量的參數(shù)。在C語言中澈魄,函數(shù)func()最后一個參數(shù)寫成省略號(...)景鼠,省略號之前的那個參數(shù)總是int,代表了要傳遞的可變參數(shù)的總個數(shù):
int func(int, ...)
{
...
}
C語言還提供了stdarg.h實現(xiàn)了可變參數(shù)功能的函數(shù)和宏一忱,具體操作步驟如下:
- 定義一個函數(shù)莲蜘,最有一個參數(shù)為省略號谭确,省略號前面的那個參數(shù)總是int,表示可變參數(shù)的個數(shù)票渠;
- 在函數(shù)定義中創(chuàng)建一個va_list類型變量逐哈,該類型是在stdarg.h頭文件中定義的;
- 使用int參數(shù)和va_start宏來初始化va_list變量為一個參數(shù)列表问顷。宏va_start是在stdarg.h頭文件定義的昂秃;
- 使用va_arg宏和va_list變量來訪問參數(shù)列表中的每個項;
- 使用宏va_end來清理賦予va_list變量的內(nèi)存杜窄。
下面函數(shù)實現(xiàn)計算數(shù)據(jù)平均值的功能:
double average(int num, ...)
{
int i;
va_list valist;
double sum = 0.0;
va_start(valist, num);
for(i = 0; i < num; i++)
sum += va_arg(valist, int);
va_end(valist);
return sum/num;
}
10.6 C命令行參數(shù)
執(zhí)行程序時肠骆,可以從命令行傳值給C程序。這些值被稱為命令行參數(shù)塞耕,它們對程序很重要蚀腿,特別是當(dāng)您想從外部控制程序,而不是在代碼內(nèi)對這些值進行硬編碼時扫外,就顯得尤為重要了莉钙。
命令行參數(shù)是使用main()函數(shù)參數(shù)來處理的,其中筛谚,argc是指傳入?yún)?shù)的個數(shù)磁玉,argv[]是一個指針數(shù)組,指向傳遞給程序的每個參數(shù)驾讲。
argv[0]存儲程序的名稱蚊伞,argv[1]是一個指向第一個命令行參數(shù)的指針,*argv[n]是最后一個參數(shù)吮铭。如果沒有提供任何參數(shù)时迫,argc將為1,否則沐兵,如果傳遞了一個參數(shù)别垮,argc 將被設(shè)置為2。
多個命令行參數(shù)之間用空格分隔扎谎,但是如果參數(shù)本身帶有空格碳想,那么傳遞參數(shù)的時候應(yīng)把參數(shù)放置在雙引號""或單引號''內(nèi)部。
10.7 內(nèi)存管理
C語言為內(nèi)存的分配和管理提供了如下接口(在stdlib.h頭文件中):
序號 | 函數(shù)和描述 |
---|---|
1 | void *calloc(int num, int size);該函數(shù)分配一個帶有num個元素的數(shù)組毁靶,每個元素的大小為size字節(jié) |
2 | void free(void *address);該函數(shù)釋放address所指向的address內(nèi)存塊 |
3 | void *malloc(int num);該函數(shù)分配一個num字節(jié)的數(shù)組胧奔,并把它們進行初始 |
4 | void *realloc(void *address, int newsize);該函數(shù)重新分配內(nèi)存,把內(nèi)存擴展到newsize大小 |