c語言結構
#include <stdio.h>
int main()
{
/* 我的第一個 C 程式 */
printf("Hello, World! \n");
return 0;
}
所有的 C 語言程式都需要包含 main() 函式。 程式碼從 main() 函式開始執行。
/* ... */ 用於註釋說明。
printf() 用於格式化輸出到螢幕。printf() 函式在 "stdio.h" 標頭檔案中宣告。
stdio.h 是一個頭檔案 (標準輸入輸出標頭檔案) , #include 是一個預處理命令,用來引入標頭檔案。 當編譯器遇到 printf() 函式時,如果沒有找到 stdio.h 標頭檔案,會發生編譯錯誤。
return 0; 語句用於表示退出程式。
C 程式主要包括以下部分:
前處理器指令
函式
變數
語句 & 表示式
註釋
編譯 & 執行 C 程式
步驟:
開啟一個文字編輯器,新增上述程式碼。
儲存檔案為 hello.c。
開啟命令提示符,進入到儲存檔案所在的目錄。
鍵入 gcc hello.c,輸入回車,編譯程式碼。
如果程式碼中沒有錯誤,命令提示符會跳到下一行,並生成 a.out 可執行檔案。
現在,鍵入 a.out 來執行程式。
您可以看到螢幕上顯示 "Hello World"。
c語言語法
C 程式由各種令牌組成,令牌可以是關鍵字、識別符號、常量、字串值,或者是一個符號。
在 C 程式中,分號是語句結束符。也就是說,每個語句必須以分號結束。它表明一個邏輯實體的結束。
c語言的註釋方式:
//:單行註釋
/**/:多行註釋
識別符號
C 識別符號是用來標識變數、函式,或任何其他使用者自定義專案的名稱。一個識別符號以字母 A-Z 或 a-z 或下劃線 _ 開始,後跟零個或多個字母、下劃線和數字(0-9)。
下表列出了 C 中的保留字。這些保留字不能作為常量名、變數名或其他識別符號名稱。
關鍵字 | 說明 |
---|---|
auto | 宣告自動變數 |
break | 跳出當前迴圈 |
case | 開關語句分支 |
char | 宣告字元型變數或函式返回值型別 |
const | 定義常量,如果一個變數被 const 修飾,那麼它的值就不能再被改變 |
continue | 結束當前迴圈,開始下一輪迴圈 |
default | 開關語句中的"其它"分支 |
do | 迴圈語句的迴圈體 |
double | 宣告雙精度浮點型變數或函式返回值型別 |
else | 條件語句否定分支(與 if 連用) |
enum | 宣告列舉型別 |
extern | 宣告變數或函式是在其它檔案或本檔案的其他位置定義 |
float | 宣告浮點型變數或函式返回值型別 |
for | 一種迴圈語句 |
goto | 無條件跳轉語句 |
if | 條件語句 |
int | 宣告整型變數或函式 |
long | 宣告長整型變數或函式返回值型別 |
register | 宣告暫存器變數 |
return | 子程式返回語句(可以帶引數,也可不帶引數) |
short | 宣告短整型變數或函式 |
signed | 宣告有符號型別變數或函式 |
sizeof | 計算資料型別或變數長度(即所佔位元組數) |
static | 宣告靜態變數 |
struct | 宣告結構體型別 |
switch | 用於開關語句 |
typedef | 用以給資料型別取別名 |
unsigned | 宣告無符號型別變數或函式 |
union | 宣告共用體型別 |
void | 宣告函式無返回值或無引數,宣告無型別指標 |
volatile | 說明變數在程式執行中可被隱含地改變 |
while | 迴圈語句的迴圈條件 |
c語言中的空格
只包含空格的行,被稱為空白行,可能帶有註釋,C 編譯器會完全忽略它。
在 C 中,空格用於描述空白符、製表符、換行符和註釋。空格分隔語句的各個部分,讓編譯器能識別語句中的某個元素(比如 int)在哪裡結束,下一個元素在哪裡開始。
c語言資料型別
變數的型別決定了變數儲存佔用的空間,以及如何解釋儲存的位模式。
C 中的型別可分為以下幾種:
序號 | 型別與描述 |
---|---|
1 | 基本型別: 它們是算術型別,包括兩種型別:整數型別和浮點型別。 |
2 | 列舉型別: 它們也是算術型別,被用來定義在程式中只能賦予其一定的離散整數值的變數。 |
3 | void 型別: 型別說明符 void 表明沒有可用的值。 |
4 | 派生型別: 它們包括:指標型別、陣列型別、結構型別、共用體型別和函式型別。 |
整數型別
下表列出了關於標準整數型別的儲存大小和值範圍的細節:
型別 | 儲存大小 | 值範圍 |
---|---|---|
char | 1 位元組 | -128 到 127 或 0 到 255 |
unsigned char | 1 位元組 | 0 到 255 |
signed char | 1 位元組 | -128 到 127 |
int | 2 或 4 位元組 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 位元組 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 位元組 | -32,768 到 32,767 |
unsigned short | 2 位元組 | 0 到 65,535 |
long | 4 位元組 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 位元組 | 0 到 4,294,967,295 |
#include <stdio.h>
#include <limits.h>
int main()
{
printf("int 儲存大小 : %lu \n", sizeof(int));
//表示式 sizeof(type) 得到物件或型別的儲存位元組大小。
//%lu 為 32 位無符號整數
return 0;
}
輸出結果:int 儲存大小 : 4
浮點型別
下表列出了關於標準浮點型別的儲存大小、值範圍和精度的細節:
型別 | 儲存大小 | 值範圍 | 精度 |
---|---|---|---|
float | 4 位元組 | 1.2E-38 到 3.4E+38 | 6 位有效位 |
double | 8 位元組 | 2.3E-308 到 1.7E+308 | 15 位有效位 |
long double | 16 位元組 | 3.4E-4932 到 1.1E+4932 | 19 位有效位 |
#include <stdio.h>
#include <float.h>
//標頭檔案 float.h 定義了巨集,在程式中可以使用這些值和其他有關實數二進位制表示的細節。
int main()
{
printf("float 儲存最大位元組數 : %lu \n", sizeof(float));
printf("float 最小值: %E\n", FLT_MIN );
printf("float 最大值: %E\n", FLT_MAX );
printf("精度值: %d\n", FLT_DIG );
//%E 為以指數形式輸出單、雙精度實數
return 0;
}
輸出結果:
float 儲存最大位元組數 : 4
float 最小值: 1.175494E-38
float 最大值: 3.402823E+38
精度值: 6
void 型別
void 型別指定沒有可用的值。它通常用於以下三種情況下:
序號 | 型別與描述 |
---|---|
1 | 函式返回為空 C 中有各種函式都不返回值,或者您可以說它們返回空。不返回值的函式的返回型別為空。例如 void exit (int status); |
2 | 函式引數為空 C 中有各種函式不接受任何引數。不帶引數的函式可以接受一個 void。例如 int rand(void); |
3 | 指標指向 void 型別為 void * 的指標代表物件的地址,而不是型別。例如,記憶體分配函式 void *malloc( size_t size ); 返回指向 void 的指標,可以轉換為任何資料型別。 |
c變數與常量
變數其實只不過是程式可操作的儲存區的名稱。C 中每個變數都有特定的型別,型別決定了變數儲存的大小和佈局,該範圍內的值都可以儲存在記憶體中,運算子可應用於變數上。
變數的名稱可以由字母、數字和下劃線字元組成。它必須以字母或下劃線開頭。大寫字母和小寫字母是不同的,因為 C 是大小寫敏感的。
有以下幾種基本的變數型別:
型別 | 描述 |
---|---|
char | 通常是一個位元組(八位), 這是一個整數型別。 |
int | 整型,4 個位元組,取值範圍 -2147483648 到 2147483647。 |
float | 單精度浮點值。單精度是這樣的格式,1位符號,8位指數,23位小數。![]() |
double | 雙精度浮點值。雙精度是1位符號,11位指數,52位小數。![]() |
void | 表示型別的缺失。 |
C 中的變數定義
變數定義就是告訴編譯器在何處建立變數的儲存,以及如何建立變數的儲存。變數定義指定一個數據型別,幷包含了該型別的一個或多個變數的列表。
int i, j, k;
char c, ch;
float f, salary;
double d;
//變數列表可以由一個或多個識別符號名稱組成,多個識別符號之間用逗號分隔。
c中的變數初始化
extern int d = 3, f = 5; // d 和 f 的宣告與初始化
int d = 3, f = 5; // 定義並初始化 d 和 f
byte z = 22; // 定義並初始化 z
char x = 'x'; // 變數 x 的值為 'x'
注意:不帶初始化的定義:帶有靜態儲存持續時間的變數會被隱式初始化為 NULL(所有位元組的值都是 0),其他所有變數的初始值是未定義的。
c中的變數宣告
變數宣告向編譯器保證變數以指定的型別和名稱存在,這樣編譯器在不需要知道變數完整細節的情況下也能繼續進一步的編譯。變數宣告只在編譯時有它的意義,在程式連線時編譯器需要實際的變數宣告。
變數的宣告有兩種情況:
1、一種是需要建立儲存空間的。例如:int a 在宣告的時候就已經建立了儲存空間。
2、另一種是不需要建立儲存空間的,通過使用extern關鍵字宣告變數名而不定義它。 例如:extern int a 其中變數 a 可以在別的檔案中定義的。
除非有extern關鍵字,否則都是變數的定義。
extern int i; //宣告,不是定義
int i; //宣告,也是定義
C 中有兩種型別的表示式:
左值(lvalue):指向記憶體位置的表示式被稱為左值(lvalue)表示式。左值可以出現在賦值號的左邊或右邊。
右值(rvalue):術語右值(rvalue)指的是儲存在記憶體中某些地址的數值。右值是不能對其進行賦值的表示式,也就是說,右值可以出現在賦值號的右邊,但不能出現在賦值號的左邊。
變數是左值,因此可以出現在賦值號的左邊。數值型的字面值是右值,因此不能被賦值,不能出現在賦值號的左邊。
c常量
常量就像是常規的變數,只不過常量的值在定義後不能進行修改。
整數常量
可以是十進位制、八進位制或十六進位制的常量。字首指定基數:0x 或 0X 表示十六進位制,0 表示八進位制,不帶字首則預設表示十進位制。
整數常量也可以帶一個字尾,字尾是 U 和 L 的組合,U 表示無符號整數(unsigned),L 表示長整數(long)。字尾可以是大寫,也可以是小寫,U 和 L 的順序任意。
212 /* 合法的 */
215u /* 合法的 */
0xFeeL /* 合法的 */
078 /* 非法的:8 不是八進位制的數字 */
032UU /* 非法的:不能重複字尾 */
浮點常量
浮點常量由整數部分、小數點、小數部分和指數部分組成。您可以使用小數形式或者指數形式來表示浮點常量。
當使用小數形式表示時,必須包含整數部分、小數部分,或同時包含兩者。當使用指數形式表示時, 必須包含小數點、指數,或同時包含兩者。帶符號的指數是用 e 或 E 引入的。
3.14159 /* 合法的 */
314159E-5L /* 合法的 */
510E /* 非法的:不完整的指數 */
210f /* 非法的:沒有小數或指數 */
.e55 /* 非法的:缺少整數或分數 */
字元常量
字元常量是括在單引號中,例如,'x' 可以儲存在 char 型別的簡單變數中。
字元常量可以是一個普通的字元(例如 'x')、一個轉義序列(例如 '\t'),或一個通用的字元(例如 '\u02C0')。
在 C 中,有一些特定的字元,當它們前面有反斜槓時,它們就具有特殊的含義,被用來表示如換行符(\n)或製表符(\t)等。下表列出了一些這樣的轉義序列碼:
轉義序列 | 含義 |
---|---|
\ | \ 字元 |
' | ' 字元 |
" | " 字元 |
\? | ? 字元 |
\a | 警報鈴聲 |
\b | 退格鍵 |
\f | 換頁符 |
\n | 換行符 |
\r | 回車 |
\t | 水平製表符 |
\v | 垂直製表符 |
\ooo | 一到三位的八進位制數 |
\xhh . . . | 一個或多個數字的十六進位制數 |
#include <stdio.h>
int main()
{
printf("Hello\tWorld\n\n");
return 0;
}
輸出結果:Hello World
字串常量
字串字面值或常量是括在雙引號 "" 中的。一個字串包含類似於字元常量的字元:普通的字元、轉義序列和通用的字元。
您可以使用空格做分隔符,把一個很長的字串常量進行分行。
"hello, dear"
"hello, \
dear"
"hello, " "d" "ear"
C儲存類
儲存類定義 C 程式中變數/函式的範圍(可見性)和生命週期。這些說明符放置在它們所修飾的型別之前。下面列出 C 程式中可用的儲存類:
- auto
//auto 儲存類是所有區域性變數預設的儲存類。
- register
// 儲存類用於定義儲存在暫存器中而不是 RAM 中的區域性變數。這意味著變數的最大尺寸等於暫存器的大小(通常是一個詞),且不能對它應用一元的 '&' 運算子(因為它沒有記憶體位置)。
- static
//儲存類指示編譯器在程式的生命週期內保持區域性變數的存在,而不需要在每次它進入和離開作用域時進行建立和銷燬。因此,使用 static 修飾區域性變數可以在函式呼叫之間保持區域性變數的值。
//static 修飾符也可以應用於全域性變數。當 static 修飾全域性變數時,會使變數的作用域限制在宣告它的檔案內。
//全域性宣告的一個 static 變數或方法可以被任何函式或方法呼叫,只要這些方法出現在跟 static 變數或方法同一個檔案中。
- extern
//儲存類用於提供一個全域性變數的引用,全域性變數對所有的程式檔案都是可見的。當您使用 extern 時,對於無法初始化的變數,會把變數名指向一個之前定義過的儲存位置。
//當您有多個檔案且定義了一個可以在其他檔案中使用的全域性變數或函式時,可以在其他檔案中使用 extern 來得到已定義的變數或函式的引用。可以這麼理解,extern 是用來在另一個檔案中宣告一個全域性變數或函式。
//extern 修飾符通常用於當有兩個或多個檔案共享相同的全域性變數或函式的時候
#include <stdio.h>
/* 函式宣告 */
void func1(void);
static int count=10; /* 全域性變數 - static 是預設的 */
int main()
{
while (count--) {
func1();
}
return 0;
}
void func1(void)
{
/* 'thingy' 是 'func1' 的區域性變數 - 只初始化一次
* 每次呼叫函式 'func1' 'thingy' 值不會被重置。
*/
static int thingy=5;
thingy++;
printf(" thingy 為 %d , count 為 %d\n", thingy, count);
}
輸出結果:
thingy 為 6 , count 為 9
thingy 為 7 , count 為 8
thingy 為 8 , count 為 7
thingy 為 9 , count 為 6
thingy 為 10 , count 為 5
thingy 為 11 , count 為 4
thingy 為 12 , count 為 3
thingy 為 13 , count 為 2
thingy 為 14 , count 為 1
thingy 為 15 , count 為 0
c運算子
運算子是一種告訴編譯器執行特定的數學或邏輯操作的符號。C 語言內建了豐富的運算子,並提供了以下型別的運算子:
算術運算子
運算子 描述 例項 + 把兩個運算元相加 A + B 將得到 30 - 從第一個運算元中減去第二個運算元 A - B 將得到 -10 * 把兩個運算元相乘 A * B 將得到 200 / 分子除以分母 B / A 將得到 2 % 取模運算子,整除後的餘數 B % A 將得到 0 ++ 自增運算子,整數值增加 1 A++ 將得到 11 -- 自減運算子,整數值減少 1 A-- 將得到 9 關係運算符
運算子 描述 例項 == 檢查兩個運算元的值是否相等,如果相等則條件為真。 (A == B) 為假。 != 檢查兩個運算元的值是否相等,如果不相等則條件為真。 (A != B) 為真。 > 檢查左運算元的值是否大於右運算元的值,如果是則條件為真。 (A > B) 為假。 < 檢查左運算元的值是否小於右運算元的值,如果是則條件為真。 (A < B) 為真。 >= 檢查左運算元的值是否大於或等於右運算元的值,如果是則條件為真。 (A >= B) 為假。 <= 檢查左運算元的值是否小於或等於右運算元的值,如果是則條件為真。 (A <= B) 為真。 邏輯運算子
運算子 描述 例項 && 稱為邏輯與運算子。如果兩個運算元都非零,則條件為真。 (A && B) 為假。 || 稱為邏輯或運算子。如果兩個運算元中有任意一個非零,則條件為真。 (A || B) 為真。 ! 稱為邏輯非運算子。用來逆轉運算元的邏輯狀態。如果條件為真則邏輯非運算子將使其為假。 !(A && B) 為真。 位運算子
位運算子作用於位,並逐位執行操作。&、 | 和 ^ 的真值表如下所示:
運算子 描述 例項 & 按位與操作,按二進位制位進行"與"運算。運算規則: 0&0=0; 0&1=0; 1&0=0; 1&1=1;
(A & B) 將得到 12,即為 0000 1100 | 按位或運算子,按二進位制位進行"或"運算。運算規則: 0|0=0; 0|1=1; 1|0=1; 1|1=1;
(A | B) 將得到 61,即為 0011 1101 ^ 異或運算子,按二進位制位進行"異或"運算。運算規則: 0^0=0; 0^1=1; 1^0=1; 1^1=0;
(A ^ B) 將得到 49,即為 0011 0001 ~ 取反運算子,按二進位制位進行"取反"運算。運算規則: ~1=-2; ~0=-1;
(~A ) 將得到 -61,即為 1100 0011,一個有符號二進位制數的補碼形式。 << 二進位制左移運算子。將一個運算物件的各二進位制位全部左移若干位(左邊的二進位制位丟棄,右邊補0)。 A << 2 將得到 240,即為 1111 0000 >> 二進位制右移運算子。將一個數的各二進位制位全部右移若干位,正數左補0,負數左補1,右邊丟棄。 A >> 2 將得到 15,即為 0000 1111 賦值運算子
運算子 描述 例項 = 簡單的賦值運算子,把右邊運算元的值賦給左邊運算元 C = A + B 將把 A + B 的值賦給 C += 加且賦值運算子,把右邊運算元加上左邊運算元的結果賦值給左邊運算元 C += A 相當於 C = C + A -= 減且賦值運算子,把左邊運算元減去右邊運算元的結果賦值給左邊運算元 C -= A 相當於 C = C - A *= 乘且賦值運算子,把右邊運算元乘以左邊運算元的結果賦值給左邊運算元 C *= A 相當於 C = C * A /= 除且賦值運算子,把左邊運算元除以右邊運算元的結果賦值給左邊運算元 C /= A 相當於 C = C / A %= 求模且賦值運算子,求兩個運算元的模賦值給左邊運算元 C %= A 相當於 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 雜項運算子↦ sizeof & 三元
運算子 | 描述 | 例項 |
---|---|---|
sizeof() | 返回變數的大小。 | sizeof(a) 將返回 4,其中 a 是整數。 |
& | 返回變數的地址。 | &a; 將給出變數的實際地址。 |
* | 指向一個變數。 | *a; 將指向一個變數。 |
? : | 條件表示式 | 如果條件為真 ? 則值為 X : 否則值為 Y |
c中運算子的優先順序
類別 | 運算子 | 結合性 |
---|---|---|
字尾 | () [] -> . ++ - - | 從左到右 |
一元 | + - ! ~ ++ - - (type)* & sizeof | 從右到左 |
乘除 | * / % | 從左到右 |
加減 | + - | 從左到右 |
移位 | << >> | 從左到右 |
關係 | < <= > >= | 從左到右 |
相等 | == != | 從左到右 |
位與 AND | & | 從左到右 |
位異或 XOR | ^ | 從左到右 |
位或 OR | | | 從左到右 |
邏輯與 AND | && | 從左到右 |
邏輯或 OR | || | 從左到右 |
條件 | ?: | 從右到左 |
賦值 | = += -= *= /= %=>>= <<= &= ^= |= | 從右到左 |
逗號 | , | 從左到右 |
#include <stdio.h>
main()
{
int a = 20;
int b = 10;
int c = 15;
int d = 5;
int e;
e = (a + b) * c / d; // ( 30 * 15 ) / 5
printf("(a + b) * c / d 的值是 %d\n", e );
e = ((a + b) * c) / d; // (30 * 15 ) / 5
printf("((a + b) * c) / d 的值是 %d\n" , e );
e = (a + b) * (c / d); // (30) * (15/5)
printf("(a + b) * (c / d) 的值是 %d\n", e );
e = a + (b * c) / d; // 20 + (150/5)
printf("a + (b * c) / d 的值是 %d\n" , e );
return 0;
}
輸出結果:
(a + b) * c / d 的值是 90
((a + b) * c) / d 的值是 90
(a + b) * (c / d) 的值是 90
a + (b * c) / d 的值是 50
c判斷
判斷結構要求程式設計師指定一個或多個要評估或測試的條件,以及條件為真時要執行的語句(必需的)和條件為假時要執行的語句(可選的)。
C 語言把任何非零和非空的值假定為 true,把零或 null 假定為 false。
判斷語句
語句 | 描述 |
---|---|
if 語句 | 一個 if 語句 由一個布林表示式後跟一個或多個語句組成。 |
if...else 語句 | 一個 if 語句 後可跟一個可選的 else 語句,else 語句在布林表示式為假時執行。 |
巢狀 if 語句 | 您可以在一個 if 或 else if 語句內使用另一個 if 或 else if 語句。 |
switch 語句 | 一個 switch 語句允許測試一個變數等於多個值時的情況。 |
巢狀 switch 語句 | 您可以在一個 switch 語句內使用另一個 switch 語句。 |
#include<stdio.h>
int main()
{
int num;
printf("輸入一個數字 : ");
scanf("%d",&num);
(num%2==0)?printf("偶數"):printf("奇數");
//三目運算子
}
c迴圈
一般情況下,語句是按順序執行的:函式中的第一個語句先執行,接著是第二個語句,依此類推。
程式語言提供了更為複雜執行路徑的多種控制結構。
迴圈語句允許我們多次執行一個語句或語句組。
迴圈型別
迴圈型別 | 描述 |
---|---|
while 迴圈 | 當給定條件為真時,重複語句或語句組。它會在執行迴圈主體之前測試條件。 |
for 迴圈 | 多次執行一個語句序列,簡化管理迴圈變數的程式碼。 |
do...while 迴圈 | 除了它是在迴圈主體結尾測試條件外,其他與 while 語句類似。 |
巢狀迴圈 | 您可以在 while、for 或 do..while 迴圈內使用一個或多個迴圈。 |
迴圈控制語句
迴圈控制語句改變你程式碼的執行順序。通過它你可以實現程式碼的跳轉。
C 提供了下列的迴圈控制語句。點選連結檢視每個語句的細節。
控制語句 | 描述 |
---|---|
break 語句 | 終止迴圈或 switch 語句,程式流將繼續執行緊接著迴圈或 switch 的下一條語句。 |
continue 語句 | 告訴一個迴圈體立刻停止本次迴圈迭代,重新開始下次迴圈迭代。 |
goto 語句 | 將控制轉移到被標記的語句。但是不建議在程式中使用 goto 語句。 |
#include <stdio.h>
//無限迴圈,ctrl+c終止一個無限迴圈
int main ()
{
for( ; ; )
{
printf("該迴圈會永遠執行下去!\n");
}
return 0;
}
c函式
函式是一組一起執行一個任務的語句。每個 C 程式都至少有一個函式,即主函式 main() ,所有簡單的程式都可以定義其他額外的函式。
您可以把程式碼劃分到不同的函式中。如何劃分程式碼到不同的函式中是由您來決定的,但在邏輯上,劃分通常是根據每個函式執行一個特定的任務來進行的。
函式宣告告訴編譯器函式的名稱、返回型別和引數。函式定義提供了函式的實際主體。
C 標準庫提供了大量的程式可以呼叫的內建函式。例如,函式 strcat() 用來連線兩個字串,函式 memcpy() 用來複制記憶體到另一個位置。
定義函式
return_type function_name( parameter list )
{
body of the function
}
在 C 語言中,函式由一個函式頭和一個函式主體組成。函式的所有組成部分:
返回型別:一個函式可以返回一個值。return_type 是函式返回的值的資料型別。有些函式執行所需的操作而不返回值,在這種情況下,return_type 是關鍵字 void。
函式名稱:這是函式的實際名稱。函式名和引數列表一起構成了函式簽名。
引數:引數就像是佔位符。當函式被呼叫時,您向引數傳遞一個值,這個值被稱為實際引數。引數列表包括函式引數的型別、順序、數量。引數是可選的,也就是說,函式可能不包含引數。
函式主體:函式主體包含一組定義函式執行任務的語句。
#include <stdio.h>
/* 函式宣告 */
int max(int num1, int num2);
int main ()
{
/* 區域性變數定義 */
int a = 100;
int b = 200;
int ret;
/* 呼叫函式來獲取最大值 */
ret = max(a, b);
printf( "Max value is : %d\n", ret );
return 0;
}
/* 函式返回兩個數中較大的那個數 */
int max(int num1, int num2)
{
/* 區域性變數宣告 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
函式引數
如果函式要使用引數,則必須宣告接受引數值的變數。這些變數稱為函式的形式引數。
形式引數就像函式內的其他區域性變數,在進入函式時被建立,退出函式時被銷燬。
當呼叫函式時,有兩種向函式傳遞引數的方式:
呼叫型別 | 描述 |
---|---|
傳值呼叫 | 該方法把引數的實際值複製給函式的形式引數。在這種情況下,修改函式內的形式引數不會影響實際引數。 |
引用呼叫 | 通過指標傳遞方式,形參為指向實參地址的指標,當對形參的指向操作時,就相當於對實參本身進行的操作。 |
預設情況下,C 使用傳值呼叫來傳遞引數。一般來說,這意味著函式內的程式碼不能改變用於呼叫函式的實際引數。
c作用域規則
任何一種程式設計中,作用域是程式中定義的變數所存在的區域,超過該區域變數就不能被訪問。C 語言中有三個地方可以宣告變數:
在函式或塊內部的區域性變數
在所有函式外部的全域性變數
在形式引數的函式引數定義中
#include <stdio.h>
/* 全域性變數宣告 */
int a = 20;
int main ()
{
/* 在主函式中的區域性變數宣告 */
int a = 10;
int b = 20;
int c = 0;
int sum(int, int);
printf ("value of a in main() = %d\n", a);
c = sum( a, b);
printf ("value of c in main() = %d\n", c);
return 0;
}
/* 新增兩個整數的函式 */
int sum(int a, int b)
{
printf ("value of a in sum() = %d\n", a);
printf ("value of b in sum() = %d\n", b);
return a + b;
}
全域性變數與區域性變數在記憶體中的區別:
全域性變數儲存在記憶體的全域性儲存區中,佔用靜態的儲存單元;
區域性變數儲存在棧中,只有在所在函式被呼叫時才動態地為變數分配儲存單元
初始化區域性變數和全域性變數
當局部變數被定義時,系統不會對其初始化,您必須自行對其初始化。定義全域性變數時,系統會自動對其初始化,如下所示:
資料型別 | 初始化預設值 |
---|---|
int | 0 |
char | '\0' |
float | 0 |
double | 0 |
pointer | NULL |
c陣列
C 語言支援陣列資料結構,它可以儲存一個固定大小的相同型別元素的順序集合。陣列是用來儲存一系列資料,但它往往被認為是一系列相同型別的變數。
陣列的宣告並不是宣告一個個單獨的變數,而是宣告一個個單獨的變數。
所有的陣列都是由連續的記憶體位置組成。最低的地址對應第一個元素,最高的地址對應最後一個元素。
初始化陣列
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//大括號 { } 之間的值的數目不能大於我們在陣列宣告時在方括號 [ ] 中指定的元素數目。
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//省略掉了陣列的大小,陣列的大小則為初始化時元素的個數。
balance[4] = 50.0;
//為陣列中某個元素賦值
#include <stdio.h>
int main ()
{
int n[ 10 ]; /* n 是一個包含 10 個整數的陣列 */
int i,j;
/* 初始化陣列元素 */
for ( i = 0; i < 10; i++ )
{
n[ i ] = i + 100; /* 設定元素 i 為 i + 100 */
}
/* 輸出陣列中每個元素的值 */
for (j = 0; j < 10; j++ )
{
printf("Element[%d] = %d\n", j, n[j] );
}
return 0;
}
輸出結果:
Element[0] = 100
Element[1] = 101
Element[2] = 102
Element[3] = 103
Element[4] = 104
Element[5] = 105
Element[6] = 106
Element[7] = 107
Element[8] = 108
Element[9] = 109
c enum(列舉)
定義格式:
enum 列舉名 {列舉元素1,列舉元素2,……};
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
//第一個列舉成員的預設值為整型的 0,後續列舉成員的值在前一個成員上加 1。
1、先定義列舉型別,再定義列舉變數
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
2、定義列舉型別的同時定義列舉變數
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
3、省略列舉名稱,直接定義列舉變數
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
#include <stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
int main()
{
enum DAY day;
day = WED;
printf("%d",day);
return 0;
}
輸出結果:3
#include <stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
int main()
{
// 遍歷列舉元素
for (day = MON; day <= SUN; day++) {
printf("列舉元素:%d \n", day);
}
}
輸出結果:
列舉元素:1
列舉元素:2
列舉元素:3
列舉元素:4
列舉元素:5
列舉元素:6
列舉元素:7
//將整數轉換為列舉
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum day
{
saturday,
sunday,
monday,
tuesday,
wednesday,
thursday,
friday
} workday;
int a = 1;
enum day weekend;
weekend = ( enum day ) a; //型別轉換
//weekend = a; //錯誤
printf("weekend:%d",weekend);
return 0;
}
//輸出結果:weekend:1
//列舉在Switch中的使用
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum color { red=1, green, blue };
enum color favorite_color;
/* 使用者輸入數字來選擇顏色 */
printf("請輸入你喜歡的顏色: (1. red, 2. green, 3. blue): ");
scanf("%u", &favorite_color);
/* 輸出結果 */
switch (favorite_color)
{
case red:
printf("你喜歡的顏色是紅色");
break;
case green:
printf("你喜歡的顏色是綠色");
break;
case blue:
printf("你喜歡的顏色是藍色");
break;
default:
printf("你沒有選擇你喜歡的顏色");
}
return 0;
}
c指標(即地址)
每一個變數都有一個記憶體位置,每一個記憶體位置都定義了可使用 & 運算子訪問的地址,它表示了在記憶體中的一個地址。
#include <stdio.h>
int main ()
{
int a = 10;
int *p; // 定義指標變數
p = &a;
printf("a 變數的地址: %p\n", p);
return 0;
}
輸出的結果:
a變數的地址:0x7ffeeaae08d8
指標也就是記憶體地址,指標變數是用來存放記憶體地址的變數。就像其他變數或常量一樣,您必須在使用指標儲存其他變數地址之前,對其進行宣告。
int *ip; /* 一個整型的指標 */
double *dp; /* 一個 double 型的指標 */
float *fp; /* 一個浮點型的指標 */
char *ch; /* 一個字元型的指標 */
#include <stdio.h>
int main ()
{
int var = 20; /* 實際變數的宣告 */
int *ip; /* 指標變數的宣告 */
ip = &var; /* 在指標變數中儲存 var 的地址 */
printf("var 變數的地址: %p\n", &var );
/* 在指標變數中儲存的地址 */
printf("ip 變數儲存的地址: %p\n", ip );
/* 使用指標訪問值 */
printf("*ip 變數的值: %d\n", *ip );
return 0;
}
輸出結果:
var 變數的地址: 0x7ffeeef168d8
ip 變數儲存的地址: 0x7ffeeef168d8
*ip 變數的值: 20
c中的NULL指標
在變數宣告的時候,如果沒有確切的地址可以賦值,為指標變數賦一個 NULL 值是一個良好的程式設計習慣。賦為 NULL 值的指標被稱為空指標。
NULL 指標是一個定義在標準庫中的值為零的常量。
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("ptr 的地址是 %p\n", ptr );
return 0;
}
輸出結果:
ptr 的地址是 0x0
注意:如需檢查一個空指標,您可以使用 if 語句,如下所示:
if(ptr) /* 如果 p 非空,則完成 */
if(!ptr) /* 如果 p 為空,則完成 */
C 指標詳解
在 C 中,有很多指標相關的概念,這些概念都很簡單,但是都很重要。下面列出了 C 程式設計師必須清楚的一些與指標相關的重要概念:
概念 | 描述 |
---|---|
指標的算術運算 | 可以對指標進行四種算術運算:++、--、+、- |
指標陣列 | 可以定義用來儲存指標的陣列。 |
指向指標的指標 | C 允許指向指標的指標。 |
傳遞指標給函式 | 通過引用或地址傳遞引數,使傳遞的引數在呼叫函式中被改變。 |
從函式返回指標 | C 允許函式返回指標到區域性變數、靜態變數和動態記憶體分配。 |
c函式指標與回撥函式
函式指標是指向函式的指標變數。
通常我們說的指標變數是指向一個整型、字元型或陣列等變數,而函式指標是指向函式。
函式指標可以像一般函式一樣,用於呼叫函式、傳遞引數。
#include <stdio.h>
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函式指標 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("請輸入三個數字:");
scanf("%d %d %d", & a, & b, & c);
/* 與直接呼叫函式等價,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的數字是: %d\n", d);
return 0;
}
輸出結果:
請輸入三個數字:1 2 3
最大的數字是: 3
回撥函式
函式指標作為某個函式的引數
函式指標變數可以作為某個函式的引數來使用的,回撥函式就是一個通過函式指標呼叫的函式。
簡單講:回撥函式是由別人的函式執行時呼叫你實現的函式。
你到一個商店買東西,剛好你要的東西沒有貨,於是你在店員那裡留下了你的電話,過了幾天店裡有貨了,店員就打了你的電話,然後你接到電話後就到店裡去取了貨。在這個例子裡,你的電話號碼就叫回調函式,你把電話留給店員就叫登記回撥函式,店裡後來有貨了叫做觸發了回撥關聯的事件,店員給你打電話叫做呼叫回撥函式,你到店裡去取貨叫做響應回撥事件。
#include <stdlib.h>
#include <stdio.h>
// 回撥函式
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
for (size_t i=0; i<arraySize; i++)
array[i] = getNextValue();
}
// 獲取隨機值
int getNextRandomValue(void)
{
return rand();
}
int main(void)
{
int myarray[10];
/* getNextRandomValue 不能加括號,否則無法編譯,因為加上括號之後相當於傳入此引數時傳入了 int , 而不是函式指標*/
populate_array(myarray, 10, getNextRandomValue);
for(int i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
}
printf("\n");
return 0;
}
輸出結果:
16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709
c字串
在 C 語言中,字串實際上是使用 null 字元 \0 終止的一維字元陣列。因此,一個以 null 結尾的字串,包含了組成字串的字元。
char site[4] = {'R', 'U', 'N', '\0'};
//兩句是等價的
char site[] = "RUN";
C 中有大量操作字串的函式:
序號 | 函式 & 目的 |
---|---|
1 | strcpy(s1, s2); 複製字串 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 的第一次出現的位置。 |
6 | strstr(s1, s2); 返回一個指標,指向字串 s1 中字串 s2 的第一次出現的位置。 |
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[14] = "runoob";
char str2[14] = "google";
char str3[14];
int len ;
/* 複製 str1 到 str3 */
strcpy(str3, str1);
printf("strcpy( str3, str1) : %s\n", str3 );
/* 連線 str1 和 str2 */
strcat( str1, str2);
printf("strcat( str1, str2): %s\n", str1 );
/* 連線後,str1 的總長度 */
len = strlen(str1);
printf("strlen(str1) : %d\n", len );
return 0;
}
輸出結果:
strcpy( str3, str1) : runoob
strcat( str1, str2): runoobgoogle
strlen(str1) : 12
c結構體
C 陣列允許定義可儲存相同型別資料項的變數,結構是 C 程式設計中另一種使用者自定義的可用的資料型別,它允許您儲存不同型別的資料項。
定義結構
struct tag {
member-list
member-list
member-list
...
} variable-list ;
//tag 是結構體標籤。
//member-list 是標準的變數定義,比如 int i; 或者 float f,或者其他有效的變數定義。
//variable-list 結構變數,定義在結構的末尾,最後一個分號之前,您可以指定一個或多個結構變數。
在一般情況下,tag、member-list、variable-list 這 3 部分至少要出現 2 個。
//此宣告聲明瞭擁有3個成員的結構體,分別為整型的a,字元型的b和雙精度的c
//同時又聲明瞭結構體變數s1
//這個結構體並沒有標明其標籤
struct
{
int a;
char b;
double c;
} s1;
//此宣告聲明瞭擁有3個成員的結構體,分別為整型的a,字元型的b和雙精度的c
//結構體的標籤被命名為SIMPLE,沒有宣告變數
struct SIMPLE
{
int a;
char b;
double c;
};
//用SIMPLE標籤的結構體,另外聲明瞭變數t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
//也可以用typedef建立新型別
typedef struct
{
int a;
char b;
double c;
} Simple2;
//現在可以用Simple2作為型別宣告新的結構體變數
Simple2 u1, u2[20], *u3;
結構體變數的初始化
#include <stdio.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 語言", "RUNOOB", "程式語言", 123456};
int main()
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}
輸出結果:
title : C 語言
author: RUNOOB
subject: 程式語言
book_id: 123456
訪問結構成員
為了訪問結構的成員,使用成員訪問運算子(.)。
#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main( )
{
struct Books Book1; /* 宣告 Book1,型別為 Books */
struct Books Book2; /* 宣告 Book2,型別為 Books */
/* Book1 詳述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 詳述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 輸出 Book1 資訊 */
printf( "Book 1 title : %s\n", Book1.title);
printf( "Book 1 author : %s\n", Book1.author);
printf( "Book 1 subject : %s\n", Book1.subject);
printf( "Book 1 book_id : %d\n", Book1.book_id);
/* 輸出 Book2 資訊 */
printf( "Book 2 title : %s\n", Book2.title);
printf( "Book 2 author : %s\n", Book2.author);
printf( "Book 2 subject : %s\n", Book2.subject);
printf( "Book 2 book_id : %d\n", Book2.book_id);
return 0;
}
輸出結果:
Book 1 title : C Programming
Book 1 author : Nuha Ali
Book 1 subject : C Programming Tutorial
Book 1 book_id : 6495407
Book 2 title : Telecom Billing
Book 2 author : Zara Ali
Book 2 subject : Telecom Billing Tutorial
Book 2 book_id : 6495700
結構作為函式引數
把結構作為函式引數,傳參方式與其他型別的變數或指標類似
#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
/* 函式宣告 */
void printBook( struct Books book );
int main( )
{
struct Books Book1; /* 宣告 Book1,型別為 Books */
struct Books Book2; /* 宣告 Book2,型別為 Books */
/* Book1 詳述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 詳述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 輸出 Book1 資訊 */
printBook( Book1 );
/* 輸出 Book2 資訊 */
printBook( Book2 );
return 0;
}
void printBook( struct Books book )
{
printf( "Book title : %s\n", book.title);
printf( "Book author : %s\n", book.author);
printf( "Book subject : %s\n", book.subject);
printf( "Book book_id : %d\n", book.book_id);
}
輸出結果:
Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
Book subject : Telecom Billing Tutorial
Book book_id : 6495700
指向結構的指標
定義指向結構的指標,方式與定義指向其他型別變數的指標相似
為了使用指向該結構的指標訪問結構的成員,必須使用 -> 運算子
#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
/* 函式宣告 */
void printBook( struct Books *book );
int main( )
{
struct Books Book1; /* 宣告 Book1,型別為 Books */
struct Books Book2; /* 宣告 Book2,型別為 Books */
/* Book1 詳述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 詳述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 通過傳 Book1 的地址來輸出 Book1 資訊 */
printBook( &Book1 );
/* 通過傳 Book2 的地址來輸出 Book2 資訊 */
printBook( &Book2 );
return 0;
}
void printBook( struct Books *book )
{
printf( "Book title : %s\n", book->title);
printf( "Book author : %s\n", book->author);
printf( "Book subject : %s\n", book->subject);
printf( "Book book_id : %d\n", book->book_id);
}
輸出結果:
Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
Book subject : Telecom Billing Tutorial
Book book_id : 6495700
c共用體
共用體是一種特殊的資料型別,允許您在相同的記憶體位置儲存不同的資料型別。可以定義一個帶有多成員的共用體,但是任何時候只能有一個成員帶有值。共用體提供了一種使用相同的記憶體位置的有效方式。
定義共用體
為了定義共用體,必須使用 union 語句,方式與定義結構類似。union 語句定義了一個新的資料型別,帶有多個成員。
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
//union tag 是可選的,每個 member definition 是標準的變數定義,比如 int i; 或者 float f; 或者其他有效的變數定義。在共用體定義的末尾,最後一個分號之前,可以指定一個或多個共用體變數,這是可選的。
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
printf( "Memory size occupied by data : %d\n", sizeof(data));
return 0;
}
輸出結果:
Memory size occupied by data : 20
訪問共用體成員
為了訪問共用體的成員,使用成員訪問運算子(.)。
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
printf( "data.i : %d\n", data.i);
data.f = 220.5;
printf( "data.f : %f\n", data.f);
strcpy( data.str, "C Programming");
printf( "data.str : %s\n", data.str);
return 0;
}
輸出結果:
data.i : 10
data.f : 220.500000
data.str : C Programming
c位域(選看-難)
#include <stdio.h>
#include <string.h>
/* 定義簡單的結構 */
struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status1;
/* 定義位域結構 */
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2;
int main( )
{
printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
printf( "Memory size occupied by status2 : %d\n", sizeof(status2));
return 0;
}
輸出結果:
Memory size occupied by status1 : 8
Memory size occupied by status2 : 4
位域宣告
有些資訊在儲存時,並不需要佔用一個完整的位元組,而只需佔幾個或一個二進位制位。例如在存放一個開關量時,只有 0 和 1 兩種狀態,用 1 位二進位即可。為了節省儲存空間,並使處理簡便,C 語言又提供了一種資料結構,稱為"位域"或"位段"。
所謂"位域"是把一個位元組中的二進位劃分為幾個不同的區域,並說明每個區域的位數。每個域有一個域名,允許在程式中按域名進行操作。這樣就可以把幾個不同的物件用一個位元組的二進位制位域來表示。
位域的定義和位域變數的說明
位域定義與結構定義相仿,其形式為:
struct 位域結構名
{
位域列表
};
其中位域列表的形式為:
type [member_name] : width ;
下面是有關位域中變數元素的描述:
元素 | 描述 |
---|---|
type | 只能為 int(整型),unsigned int(無符號整型),signed int(有符號整型) 三種類型,決定了如何解釋位域的值。 |
member_name | 位域的名稱。 |
width | 位域中位的數量。寬度必須小於或等於指定型別的位寬度。 |
#include <stdio.h>
#include <string.h>
struct
{
unsigned int age : 3;
} Age;
int main( )
{
Age.age = 4;
printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
printf( "Age.age : %d\n", Age.age );
Age.age = 7;
printf( "Age.age : %d\n", Age.age );
Age.age = 8; // 二進位制表示為 1000 有四位,超出
printf( "Age.age : %d\n", Age.age );
return 0;
}
輸出結果:
Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0
位域的使用
位域的使用和結構成員的使用相同,其一般形式為:
位域變數名.位域名
位域變數名->位域名
#include<stdio.h>
#include <string.h>
int main(){
struct bs{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1; /* 給位域賦值(應注意賦值不能超過該位域的允許範圍) */
bit.b=7; /* 給位域賦值(應注意賦值不能超過該位域的允許範圍) */
bit.c=15; /* 給位域賦值(應注意賦值不能超過該位域的允許範圍) */
printf("%d,%d,%d\n",bit.a,bit.b,bit.c); /* 以整型量格式輸出三個域的內容 */
pbit=&bit; /* 把位域變數 bit 的地址送給指標變數 pbit */
pbit->a=0; /* 用指標方式給位域 a 重新賦值,賦為 0 */
pbit->b&=3; /* 使用了複合的位運算子 "&=",相當於:pbit->b=pbit->b&3,位域 b 中原有值為 7,與 3 作按位與運算的結果為 3(111&011=011,十進位制值為 3) */
pbit->c|=1; /* 使用了複合位運算子"|=",相當於:pbit->c=pbit->c|1,其結果為 15 */
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c); /* 用指標方式輸出了這三個域的值 */
}
c typedef
C 語言提供了 typedef 關鍵字,您可以使用它來為型別取一個新的名字。
#include <stdio.h>
#include <string.h>
typedef struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} Book;
int main( )
{
Book book;
strcpy( book.title, "C 教程");
strcpy( book.author, "Runoob");
strcpy( book.subject, "程式語言");
book.book_id = 12345;
printf( "書標題 : %s\n", book.title);
printf( "書作者 : %s\n", book.author);
printf( "書類目 : %s\n", book.subject);
printf( "書 ID : %d\n", book.book_id);
return 0;
}
輸出結果:
書標題 : C 教程
書作者 : Runoob
書類目 : 程式語言
書 ID : 12345
typedef vs #define
#define 是 C 指令,用於為各種資料型別定義別名,與 typedef 類似,但是它們有以下幾點不同:
typedef 僅限於為型別定義符號名稱,#define 不僅可以為型別定義別名,也能為數值定義別名,比如您可以定義 1 為 ONE。
typedef 是由編譯器執行解釋的,#define 語句是由預編譯器進行處理的。
#include <stdio.h>
#define TRUE 1
#define FALSE 0
int main( )
{
printf( "TRUE 的值: %d\n", TRUE);
printf( "FALSE 的值: %d\n", FALSE);
return 0;
}
輸出結果:
TRUE 的值: 1
FALSE 的值: 0
c 輸入&輸出
當我們提到輸入時,這意味著要向程式填充一些資料。輸入可以是以檔案的形式或從命令列中進行。C 語言提供了一系列內建的函式來讀取給定的輸入,並根據需要填充到程式中。
當我們提到輸出時,這意味著要在螢幕上、印表機上或任意檔案中顯示一些資料。C 語言提供了一系列內建的函式來輸出資料到計算機螢幕上和儲存資料到文字檔案或二進位制檔案中。
標準檔案
C 語言把所有的裝置都當作檔案。所以裝置(比如顯示器)被處理的方式與檔案相同。以下三個檔案會在程式執行時自動開啟,以便訪問鍵盤和螢幕。
標準檔案 | 檔案指標 | 裝置 |
---|---|---|
標準輸入 | stdin | 鍵盤 |
標準輸出 | stdout | 螢幕 |
標準錯誤 | stderr | 您的螢幕 |
檔案指標是訪問檔案的方式,本節將講解如何從螢幕讀取值以及如何把結果輸出到螢幕上。
C 語言中的 I/O (輸入/輸出) 通常使用 printf() 和 scanf() 兩個函式。
scanf() 函式用於從標準輸入(鍵盤)讀取並格式化, printf() 函式傳送格式化輸出到標準輸出(螢幕)。
#include <stdio.h>
int main( ) {
char str[100];
int i;
printf( "Enter a value :");
scanf("%s %d", str, &i);
printf( "\nYou entered: %s %d ", str, i);
printf("\n");
return 0;
}
//scanf() 在讀取字串時,只要遇到一個空格,scanf() 就會停止讀取
gets() & puts() 函式
char *gets(char *s) 函式從 stdin 讀取一行到 s 所指向的緩衝區,直到一個終止符或 EOF。
int puts(const char *s) 函式把字串 s 和一個尾隨的換行符寫入到 stdout。
#include <stdio.h>
int main( )
{
char str[100];
printf( "Enter a value :");
gets( str );
printf( "\nYou entered: ");
puts( str );
return 0;
}
//當上面的程式碼被編譯和執行時,它會等待您輸入一些文字,當您輸入一個文字並按下回車鍵時,程式會繼續並讀取一整行直到該行結束
getchar() & putchar() 函式
#include <stdio.h>
int main( )
{
int c;
printf( "Enter a value :");
c = getchar( );
printf( "\nYou entered: ");
putchar( c );
printf( "\n");
return 0;
}
//輸入一個文字並按下回車鍵時,程式會繼續並只會讀取一個單一的字元
c 檔案讀寫(難)
一個檔案,無論它是文字檔案還是二進位制檔案,都是代表了一系列的位元組。C 語言不僅提供了訪問頂層的函式,也提供了底層(OS)呼叫來處理儲存裝置上的檔案。
開啟檔案
可以使用 fopen( ) 函式來建立一個新的檔案或者開啟一個已有的檔案,這個呼叫會初始化型別 FILE 的一個物件,型別 FILE 包含了所有用來控制流的必要的資訊。下面是這個函式呼叫的原型:
FILE *fopen( const char * filename, const char * mode );
filename 是字串,用來命名檔案,訪問模式 mode 的值可以是下列值中的一個:
模式 | 描述 |
---|---|
r | 開啟一個已有的文字檔案,允許讀取檔案。 |
w | 開啟一個文字檔案,允許寫入檔案。如果檔案不存在,則會建立一個新檔案。在這裡,您的程式會從檔案的開頭寫入內容。如果檔案存在,則該會被截斷為零長度,重新寫入。 |
a | 開啟一個文字檔案,以追加模式寫入檔案。如果檔案不存在,則會建立一個新檔案。在這裡,您的程式會在已有的檔案內容中追加內容。 |
r+ | 開啟一個文字檔案,允許讀寫檔案。 |
w+ | 開啟一個文字檔案,允許讀寫檔案。如果檔案已存在,則檔案會被截斷為零長度,如果檔案不存在,則會建立一個新檔案。 |
a+ | 開啟一個文字檔案,允許讀寫檔案。如果檔案不存在,則會建立一個新檔案。讀取會從檔案的開頭開始,寫入則只能是追加模式。 |
關閉檔案
為了關閉檔案,請使用 fclose( ) 函式。函式的原型如下:
int fclose( FILE *fp );
//如果成功關閉檔案,fclose( ) 函式返回零,如果關閉檔案時發生錯誤,函式返回 EOF。這個函式實際上,會清空緩衝區中的資料,關閉檔案,並釋放用於該檔案的所有記憶體。EOF 是一個定義在標頭檔案 stdio.h 中的常量。
//C 標準庫提供了各種函式來按字元或者以固定長度字串的形式讀寫檔案。
寫入檔案
int fputc( int c, FILE *fp );
//注意:請確保您有可用的 tmp 目錄,如果不存在該目錄,則需要在您的計算機上先建立該目錄。
/tmp 一般是 Linux 系統上的臨時目錄,如果你在 Windows 系統上執行,則需要修改為本地環境中已存在的目錄,例如: C:\tmp、D:\tmp等。
#include <stdio.h>
int main()
{
FILE *fp = NULL;
fp = fopen("/tmp/test.txt", "w+");
fprintf(fp, "This is testing for fprintf...\n");
fputs("This is testing for fputs...\n", fp);
fclose(fp);
}
讀取檔案
int fgetc( FILE * fp );
//fgetc() 函式從 fp 所指向的輸入檔案中讀取一個字元。返回值是讀取的字元,如果發生錯誤則返回 EOF。
函式允許您從流中讀取一個字串:
char *fgets( char *buf, int n, FILE *fp );
函式 fgets() 從 fp 所指向的輸入流中讀取 n - 1 個字元。它會把讀取的字串複製到緩衝區 buf,並在最後追加一個 null 字元來終止字串。
如果這個函式在讀取最後一個字元之前就遇到一個換行符 '\n' 或檔案的末尾 EOF,則只會返回讀取到的字元,包括換行符。您也可以使用 int fscanf(FILE *fp, const char *format, ...) 函式來從檔案中讀取字串,但是在遇到第一個空格和換行符時,它會停止讀取。
#include <stdio.h>
int main()
{
FILE *fp = NULL;
char buff[255];
fp = fopen("/tmp/test.txt", "r");
fscanf(fp, "%s", buff);
printf("1: %s\n", buff );
fgets(buff, 255, (FILE*)fp);
printf("2: %s\n", buff );
fgets(buff, 255, (FILE*)fp);
printf("3: %s\n", buff );
fclose(fp);
}
//分析:首先,fscanf() 方法只讀取了 This,因為它在後邊遇到了一個空格。其次,呼叫 fgets() 讀取剩餘的部分,直到行尾。最後,呼叫 fgets() 完整地讀取第二行。
輸出結果:
1: This
2: is testing for fprintf...
3: This is testing for fputs...
c 前處理器
C 前處理器不是編譯器的組成部分,但是它是編譯過程中一個單獨的步驟。簡言之,C 前處理器只不過是一個文字替換工具而已,它們會指示編譯器在實際編譯之前完成所需的預處理。我們將把 C 前處理器(C Preprocessor)簡寫為 CPP。
所有的前處理器命令都是以井號(#)開頭。它必須是第一個非空字元,為了增強可讀性,前處理器指令應從第一列開始。下面列出了所有重要的前處理器指令:
指令 | 描述 |
---|---|
#define | 定義巨集 |
#include | 包含一個原始碼檔案 |
#undef | 取消已定義的巨集 |
#ifdef | 如果巨集已經定義,則返回真 |
#ifndef | 如果巨集沒有定義,則返回真 |
#if | 如果給定條件為真,則編譯下面程式碼 |
#else | #if 的替代方案 |
#elif | 如果前面的 #if 給定條件不為真,當前條件為真,則編譯下面程式碼 |
#endif | 結束一個 #if……#else 條件編譯塊 |
#error | 當遇到標準錯誤時,輸出錯誤訊息 |
#pragma | 使用標準化方法,向編譯器釋出特殊的命令到編譯器中 |
前處理器運算子(難)
C 前處理器提供了下列的運算子來幫助您建立巨集:
巨集延續運算子(\)
一個巨集通常寫在一個單行上。但是如果巨集太長,一個單行容納不下,則使用巨集延續運算子(\)。例如:
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
字串常量化運算子(#)
在巨集定義中,當需要把一個巨集的引數轉換為字串常量時,則使用字串常量化運算子(#)。在巨集中使用的該運算子有一個特定的引數或引數列表。例如:
#include <stdio.h>
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
int main(void)
{
message_for(Carole, Debra);
return 0;
}
當上面的程式碼被編譯和執行時,它會產生下列結果:
Carole and Debra: We love you!
標記貼上運算子(##)
巨集定義內的標記貼上運算子(##)會合並兩個引數。它允許在巨集定義中兩個獨立的標記被合併為一個標記。例如:
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
int main(void)
{
int token34 = 40;
tokenpaster(34);
return 0;
}
當上面的程式碼被編譯和執行時,它會產生下列結果:
token34 = 40
這是怎麼發生的,因為這個例項會從編譯器產生下列的實際輸出:
printf ("token34 = %d", token34);
這個例項演示了 token##n 會連線到 token34 中,在這裡,我們使用了字串常量化運算子(#)和標記貼上運算子(##)。
defined() 運算子
前處理器 defined 運算子是用在常量表達式中的,用來確定一個識別符號是否已經使用 #define 定義過。如果指定的識別符號已定義,則值為真(非零)。如果指定的識別符號未定義,則值為假(零)。下面的例項演示了 defined() 運算子的用法:
#include <stdio.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void)
{
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
當上面的程式碼被編譯和執行時,它會產生下列結果:
Here is the message: You wish!
引數化的巨集
CPP 一個強大的功能是可以使用引數化的巨集來模擬函式。例如,下面的程式碼是計算一個數的平方:
int square(int x) {
return x * x;
}
我們可以使用巨集重寫上面的程式碼,如下:
#define square(x) ((x) * (x))
在使用帶有引數的巨集之前,必須使用 #define 指令定義。引數列表是括在圓括號內,且必須緊跟在巨集名稱的後邊。巨集名稱和左圓括號之間不允許有空格。例如:
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
輸出結果:
Max between 20 and 10 is 20
c 標頭檔案
標頭檔案是副檔名為 .h 的檔案,包含了 C 函式宣告和巨集定義,被多個原始檔中引用共享。有兩種型別的標頭檔案:程式設計師編寫的標頭檔案和編譯器自帶的標頭檔案。
在程式中要使用標頭檔案,需要使用 C 預處理指令 #include 來引用它。前面我們已經看過 stdio.h 標頭檔案,它是編譯器自帶的標頭檔案。
引用標頭檔案相當於複製標頭檔案的內容,但是我們不會直接在原始檔中複製標頭檔案的內容,因為這麼做很容易出錯,特別在程式是由多個原始檔組成的時候。
引用標頭檔案的語法
#include <file>
//用於引用系統標頭檔案。它在系統目錄的標準列表中搜索名為 file 的檔案。
#include "file"====速度快
//用於引用使用者標頭檔案。它在包含當前檔案的目錄中搜索名為 file 的檔案。
#include < > 引用的是編譯器的類庫路徑裡面的標頭檔案。
#include " " 引用的是你程式目錄的相對路徑中的標頭檔案,如果在程式目錄沒有找到引用的標頭檔案則到編譯器的類庫路徑的目錄下找該標頭檔案。
引用標頭檔案的操作(未做)
c強制型別轉換
變數的根本型別並未改變,只是輸出形式暫時改變了而已。
強制型別轉換是把變數從一種型別轉換為另一種資料型別。例如,如果您想儲存一個 long 型別的值到一個簡單的整型中,需要把 long 型別強制轉換為 int 型別。可以使用強制型別轉換運算子來把值顯式地從一種型別轉換為另一種型別。
#include <stdio.h>
int main()
{
int sum = 17, count = 5;
double mean;
mean = (double) sum / count;
printf("Value of mean : %f\n", mean );
}
輸出結果:
Value of mean : 3.400000
常用的算術轉換是隱式地把值強制轉換為相同的型別。編譯器首先執行整數提升,如果運算元型別不同,則它們會被轉換為下列層次中出現的最高層次的型別:
#include <stdio.h>
int main()
{
int i = 17;
char c = 'c'; /* ascii 值是 99 */
float sum;
sum = i + c;
printf("Value of sum : %f\n", sum );
}
輸出結果:
Value of sum : 116.000000
//算術轉換不適用於賦值運算子、邏輯運算子 && 和 ||。
c錯誤處理
C 語言不提供對錯誤處理的直接支援,但是作為一種系統程式語言,它以返回值的形式允許您訪問底層資料。在發生錯誤時,大多數的 C 或 UNIX 函式呼叫返回 1 或 NULL,同時會設定一個錯誤程式碼 errno,該錯誤程式碼是全域性變數,表示在函式呼叫期間發生了錯誤。
為什麼最後要加一句return 0
C 程式設計師可以通過檢查返回值,然後根據返回值決定採取哪種適當的動作。開發人員應該在程式初始化時,把 errno 設定為 0,這是一種良好的程式設計習慣。0 值表示程式中沒有錯誤。
errno、perror() 和 strerror()
C 語言提供了 perror() 和 strerror() 函式來顯示與 errno 相關的文字訊息。
perror() 函式顯示您傳給它的字串,後跟一個冒號、一個空格和當前 errno 值的文字表示形式。
strerror() 函式,返回一個指標,指標指向當前 errno 值的文字表示形式。
//嘗試開啟一個檔案不存在
#include <stdio.h>
#include <errno.h>
#include <string.h>
extern int errno ;
int main ()
{
FILE * pf;
int errnum;
pf = fopen ("unexist.txt", "rb");
if (pf == NULL)
{
errnum = errno;
fprintf(stderr, "錯誤號: %d\n", errno);
perror("通過 perror 輸出錯誤");
fprintf(stderr, "開啟檔案錯誤: %s\n", strerror( errnum ));
}
else
{
fclose (pf);
}
return 0;
}
輸出結果:
錯誤號: 2
通過 perror 輸出錯誤: No such file or directory
開啟檔案錯誤: No such file or directory
//被除數為0的錯誤
#include <stdio.h>
#include <stdlib.h>
main()
{
int dividend = 20;
int divisor = 0;
int quotient;
if( divisor == 0){
fprintf(stderr, "除數為 0 退出執行...\n");
exit(-1);
}
quotient = dividend / divisor;
fprintf(stderr, "quotient 變數的值為 : %d\n", quotient );
exit(0);
}
//exit(0);等價於renturn 0;但是exit執行的速度快,因為exit函式內建在errno庫裡
輸出結果:
除數為 0 退出執行...
通常情況下,程式成功執行完一個操作正常退出的時候會帶有值 EXIT_SUCCESS。在這裡,EXIT_SUCCESS 是巨集,它被定義為 0。
如果程式中存在一種錯誤情況,當您退出程式時,會帶有狀態值 EXIT_FAILURE,被定義為 -1。
#include <stdio.h>
#include <stdlib.h>
main()
{
int dividend = 20;
int divisor = 5;
int quotient;
if( divisor == 0){
fprintf(stderr, "除數為 0 退出執行...\n");
exit(EXIT_FAILURE);
}
quotient = dividend / divisor;
fprintf(stderr, "quotient 變數的值為: %d\n", quotient );
exit(EXIT_SUCCESS);
}
輸出結果:
quotient 變數的值為 : 4
c遞迴
理解:函式遞迴就是其函式本身進行的迴圈。
遞迴指的是在函式的定義中使用函式自身的方法。
語法格式
void recursion()
{
statements;
... ... ...
recursion(); /* 函式呼叫自身 */
... ... ...
}
int main()
{
recursion();
}
//階乘
#include <stdio.h>
double factorial(unsigned int i)
{
if(i <= 1)
{
return 1;
}
return i * factorial(i - 1);
}
int main()
{
int i = 5;
printf("%d 的階乘為 %f\n", i, factorial(i));
return 0;
}
輸出結果:
5 的階乘為 120.000000
//斐波那契數列==兔子問題
#include <stdio.h>
int fibonaci(int i)
{
if(i == 0)
{
return 0;
}
if(i == 1)
{
return 1;
}
return fibonaci(i-1) + fibonaci(i-2);
}
int main()
{
int i;
for (i = 0; i < 10; i++)
{
printf("%d\t\n", fibonaci(i));
}
return 0;
}
c可變引數==慢慢理解
#include <stdio.h>
#include <stdarg.h>
double average(int num,...)
{
va_list valist;
double sum = 0.0;
int i;
/* 為 num 個引數初始化 valist */
va_start(valist, num);
/* 訪問所有賦給 valist 的引數 */
for (i = 0; i < num; i++)
{
sum += va_arg(valist, int);
}
/* 清理為 valist 保留的記憶體 */
va_end(valist);
return sum/num;
}
int main()
{
printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}
//當上面的程式碼被編譯和執行時,它會產生下列結果。應該指出的是,函式 average() 被呼叫兩次,每次第一個引數都是表示被傳的可變引數的總數。省略號被用來傳遞可變數量的引數。
輸出結果:
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000
c記憶體管理
C 語言為記憶體的分配和管理提供了幾個函式。這些函式可以在 <stdlib.h> 標頭檔案中找到。
序號 | 函式和描述 |
---|---|
1 | void *calloc(int num, int size); 在記憶體中動態地分配 num 個長度為 size 的連續空間,並將每一個位元組都初始化為 0。所以它的結果是分配了 num*size 個位元組長度的記憶體空間,並且每個位元組的值都是0。 |
2 | void free(void *address); 該函式釋放 address 所指向的記憶體塊,釋放的是動態分配的記憶體空間。 |
3 | void *malloc(int num); 在堆區分配一塊指定大小的記憶體空間,用來存放資料。這塊記憶體空間在函式執行完成後不會被初始化,它們的值是未知的。 |
4 | void *realloc(void *address, int newsize); 該函式重新分配記憶體,把記憶體擴充套件到 newsize。 |
注意:void * 型別表示未確定型別的指標。C、C++ 規定 void * 型別可以通過型別轉換強制轉換為任何其它型別的指標。
//如果預先不知道需要儲存的文字長度,需要定義一個指標,該指標指向未定義所需記憶體大小的字元,後續再根據需求來分配記憶體
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char name[100];
char *description;
strcpy(name, "Zara Ali");
/* 動態分配記憶體 */
description = (char *)malloc( 200 * sizeof(char) ); //malloc替換為calloc也可
//calloc(200, sizeof(char));
if( description == NULL )
{
fprintf(stderr, "Error - unable to allocate required memory\n");
}
else
{
strcpy( description, "Zara ali a DPS student in class 10th");
}
printf("Name = %s\n", name );
printf("Description: %s\n", description );
}
輸出結果:
Name = Zara Ali
Description: Zara ali a DPS student in class 10th
重新調整記憶體的大小和釋放記憶體
當程式退出時,作業系統會自動釋放所有分配給程式的記憶體,但是,建議您在不需要記憶體時,都應該呼叫函式 free() 來釋放記憶體。
或者,您可以通過呼叫函式 realloc() 來增加或減少已分配的記憶體塊的大小。讓我們使用 realloc() 和 free() 函式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char name[100];
char *description;
strcpy(name, "Zara Ali");
/* 動態分配記憶體 */
description = (char *)malloc( 30 * sizeof(char) );
if( description == NULL )
{
fprintf(stderr, "Error - unable to allocate required memory\n");
}
else
{
strcpy( description, "Zara ali a DPS student.");
}
/* 假設您想要儲存更大的描述資訊 */
description = (char *) realloc( description, 100 * sizeof(char) );
if( description == NULL )
{
fprintf(stderr, "Error - unable to allocate required memory\n");
}
else
{
strcat( description, "She is in class 10th");
}
printf("Name = %s\n", name );
printf("Description: %s\n", description );
/* 使用 free() 函式釋放記憶體 */
free(description);
}
輸出結果:
Name = Zara Ali
Description: Zara ali a DPS student.She is in class 10th
c排序演算法
1、冒泡法
氣泡排序(英語:Bubble Sort)是一種簡單的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序(如從大到小、首字母從A到Z)錯誤就把他們交換過來。
#include <stdio.h>
void main(){
int i,j,t,a[10]; //定義變數陣列基本型別
printf("Please input numbers: ");
for(i=0;i<10;i++){
scanf("%d",&a[i]); //從鍵盤輸入要排序的陣列
}
for(i=0;i<10;i++){
for(j=i+1;j<=9;j++){
if(a[i]>a[j]){ //如果後一個數比前一個數大利用中間變數t實現倆值互換
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
}
printf("Please output numbers: ");
for(i=0;i<=9;i++){
printf("%-3d",a[i]);
}
printf("\n");
}
2、選擇排序
選擇排序(Selection sort)是一種簡單直觀的排序演算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
#include<stdio.h>
#include<stdlib.h>
void sort_min(int arr[], int length);
void sort_min(int arr[], int length)
{
int i, j;
int temp;
for(i=0; i<length-1; i++)
{
for(j=i+1, j<length; j++)
{
if(arr[i]>arr[j])
{
// 將較小的數放到索引i處,一個迴圈後,就i處就為該迴圈的最小值
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
}
int main()
{
int arr_num[7] = {55, 17, 11, 65, 32, 9, 25};
sort_min(arr_num, 7);
for(int i=0; i<7; i++)
printf("%d ", arr_num[i]);
printf("\n");
system("pause");
return 0;
}
3、插入排序
插入排序(英語:Insertion Sort)是一種簡單直觀的排序演算法。它的工作原理是通過構建有序序列,對於未排序資料,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,通常採用in-place排序(即只需用到 {\displaystyle O(1)} {\displaystyle O(1)}的額外空間的排序),因而在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,為最新元素提供插入空間。
//插入排序(從小到大)
#include<stdio.h>
int number[100000000];
int main()
{
int i=0,n,ii=0,temp=0;
printf("輸入數字個數:\n");
scanf("%d",&n);
printf("輸入%d個數:\n",n);
for(int j=0;j<n;j++)
scanf("%d",&number[j]) ;
for(i=1;i<n;i++)
{
temp=number[i];
ii=i-1;
while(ii>=0&&temp<number[ii]) //在這裡改順序 !!!
{
number[ii+1]=number[ii];
ii--;
}
number[ii+1]=temp;
}
for(i=0;i<n-1;i++)
printf("%d ",number[i]);
printf("%d\n",number[i]);
return 0;
}
4、希爾排序
希爾排序,也稱遞減增量排序演算法,是插入排序的一種更高效的改進版本。希爾排序是非穩定排序演算法。
希爾排序是基於插入排序的以下兩點性質而提出改進方法的:
插入排序在對幾乎已經排好序的資料操作時,效率高,即可以達到線性排序的效率
但插入排序一般來說是低效的,因為插入排序每次只能將資料移動一位
#include <stdio.h>
#include <malloc.h>
void shellSort(int *a, int len); // 函式宣告
int main(void)
{
int i, len, * a;
printf("請輸入要排的數的個數:");
scanf("%d",&len);
a = (int *)malloc(len * sizeof(int)); // 動態定義陣列
printf("請輸入要排的數:\n");
for (i = 0; i < len; i++) { // 陣列值的輸入
scanf("%d",&a[i]);
}
shellSort(a, len); // 呼叫希爾排序函式
printf("希爾升序排列後結果為:\n");
for (i = 0; i < len; i++) { // 排序後的結果的輸出
printf("%d\t",a[i]);
}
printf("\n");
return 0;
}
void shellSort(int *a, int len)
{
int i, j, k, tmp, gap; // gap 為步長
for (gap = len / 2; gap > 0; gap /= 2) { // 步長初始化為陣列長度的一半,每次遍歷後步長減半,
for (i = 0; i < gap; ++i) { // 變數 i 為每次分組的第一個元素下標
for (j = i + gap; j < len; j += gap) { //對步長為gap的元素進行直插排序,當gap為1時,就是直插排序
tmp = a[j]; // 備份a[j]的值
k = j - gap; // j初始化為i的前一個元素(與i相差gap長度)
while (k >= 0 && a[k] > tmp) {
a[k + gap] = a[k]; // 將在a[i]前且比tmp的值大的元素向後移動一位
k -= gap;
}
a[k + gap] = tmp;
}
}
}
}
6、歸併排序
//歸併排序(從小到大)
#include <stdio.h>
int a[3001000]; //在主函式外定義陣列
int c[3001000];
void merge_sort(int left,int right) //定義歸併函式"merge_sort"
{
if ( left == right ) return; //判斷是否只有一個數
int mid = ( left + right ) / 2; //取一箇中間數
merge_sort(left, mid); //這兩行將輸入的數列強行二分
merge_sort(mid + 1,right);
int i = left; //把開始和中間的值儲存在別的變數中
int j = mid + 1;
int len = 0;
while (i <= mid && j <= right) //在範圍內判斷前後兩數的大小
{
if (a[i] < a[j]) //判斷大小 大到小"<",小到大">"!!!
{
c[len] = a[i]; //如果條件成立(這裡是後數比前數小)把後面的值賦到前面
len++; //表示判斷過一遍
i++; //當i與下面的j其中有一個不滿足上面while後的條件則跳出迴圈,表示排序完成
}
else
{
c[len] = a[j]; //不成立就不變
len++;
j++;
}
}
for (;i<=mid;i++) //下面幾個for迴圈把排序好的數記錄下來
{
c[len] = a[i];
len++; //挨個賦值
}
for (;j<=right;j++)
{
c[len] = a[j];
len++;
}
for (int ii = left; ii <= right ;ii++)
a[ii] = c[ii - left];
}
int main() //主函式
{
int n;
printf("輸入數字個數:\n");
scanf("%d",&n); //輸入要排序的數字個數
printf("輸入%d個數:\n",n);
for (int i = 0 ; i < n ; i++) //迴圈輸入
scanf("%d",&a[i]);
merge_sort(0,n-1); //呼叫歸併排序函式"merge_sort"
for (int i = 0 ; i < n ; i++) //迴圈輸出
{
if(i!=0) //第一個數前面不加空格
printf(" ");
printf("%d",a[i]);
}
return 0;
}
//ENDING
7、快速排序
//快速排序(我把它做成了一個小程式,複製下來可以直接執行)
//感謝百度百科,本程式借鑑了一下
#include<stdio.h>
#include<string.h> //字串標頭檔案
int a[1000001]; //在主函式外面定義陣列可以大一點(很多很多)
void kuaisu_sheng(int left,int right)
{
if(left>=right) //如果左邊索引大於或者等於右邊的索引就代表已經整理完成一個組了
return ;
int i=left; //將區間記錄下來
int j=right;
int key=a[i]; //記錄參考值
while(i<j) //控制在當組內尋找一遍
{
while(i<j&&key<=a[j]) //而尋找結束的條件就是,1,找到一個小於或者大於key的數(大於或小於取決於你想升序還是降序)2,沒有符合條件1的,並且i與j的大小沒有反轉
j--; //向前尋找
a[i]=a[j]; //找到一個這樣的數後就把它賦給前面的被拿走的i的值(如果第一次迴圈且key是a[left],那麼就是給key)
while(i<j&&key>=a[i]) //這是i在當組內向前尋找,同上,不過注意與key的大小關係停止迴圈和上面相反,因為排序思想是把數往兩邊扔,所以左右兩邊的數大小與key的關係相反
i++;
a[j]=a[i];
}
a[i]=key; //當在當組內找完一遍以後就把中間數key迴歸
kuaisu_sheng(left,i-1); //最後用同樣的方式對分出來的左邊的小組進行同上的做法
kuaisu_sheng(i+1,right); //用同樣的方式對分出來的右邊的小組進行同上的做法
//當然最後可能會出現很多分左右,直到每一組的i = j 為止
}
void kuaisu_jiang(int left,int right)
{
if(left>=right)
return ;
int i=left;
int j=right;
int key=a[i];
while(i<j)
{
while(i<j&&key>=a[j]) //順序這裡改
j--;
a[i]=a[j];
while(i<j&&key<=a[i]) //還有這裡(不清楚怎麼改可以對照上面升序的函式)
i++;
a[j]=a[i];
}
a[i]=key;
kuaisu_jiang(left,i-1);
kuaisu_jiang(i+1,right);
}
int main()
{
int n,m,i,choice;
printf("快速排序小程式(By STY)\n\n");
printf("升序還是降序\n");
printf("升序選“1 ”\n");
printf("降序選“2 ”\n");
printf("請輸入:(1~2)\n");
printf("退出請按“Ctrl+Z ”(在鍵盤上),並按回車,謝謝使用!\n");
while(scanf("%d",&choice)!=EOF)
{
memset(a,0,sizeof(a)); //清空陣列
printf("請輸入要排序的數字個數:\n");
scanf("%d",&n);
printf("請輸入%d個數字:\n",n);
if(choice==1)
{
for(int ii=0;ii<n;ii++)
scanf("%d",&a[ii]);
kuaisu_sheng(0,n); //引用函式
printf("結果:\n");
for(i=1;i<n;i++) //迴圈輸出
printf("%d ",a[i]);
printf("%d(升序)\n\n\n\n",a[i]);
}
else if(choice==2)
{
for(int ii=0;ii<n;ii++)
scanf("%d",&a[ii]);
kuaisu_jiang(0,n);
printf("結果:\n");
for(i=0;i<n-1;i++)
printf("%d ",a[i]);
printf("%d(降序)\n\n\n\n",a[i]);
}
else
printf("開玩笑吧!\n");
printf("快速排序小程式(By STY)\n\n");
printf("升序還是降序\n");
printf("升序選“1”\n");
printf("降序選“2”\n");
printf("請輸入:(1~2)\n");
printf("退出請按“Ctrl+Z”(在鍵盤上),並按回車,謝謝使用!\n");
}
return 0;
}
//ENDING
在區間中隨機挑選一個元素作基準,將小於基準的元素放在基準之前,大於基準的元素放在基準之後,再分別對小數區與大數區進行排序。