前面瞭解瞭如何使用#include
引入其他檔案,接著來了解一下系統提供的一些常用庫。
字串
計算字串長度:
#include <stdio.h>
#include <string.h>
int main() {
char* c = "Hello World!";
// 使用strlen計算長度,注意返回值型別是size_t(別名而已,本質上就是unsigned long)
printf("%lu", strlen(c));
}
12
拼接字串:
#include <stdio.h>
#include <string.h>
int main() {
// 現在有兩個字串,但是我們希望把他們拼接到一起
// 注意不能這樣寫 char* a = "Hello",*b = "World!";
// 如果直接用指標指向字串常量,是無法進行拼接的,因為大小已經固定了
// 這裡需要兩個引數,第一個是目標字串,將第二個引數的字串拼接到第一個字串中(注意要裝得下才行)
char a[20] = "Hello", *b = "World!";
strcat(a, b);
printf("%s", a);
}
HelloWorld!
複製字串:
#include <stdio.h>
#include <string.h>
int main() {
char str[10], *c = "Hello";
// 使用cpy會直接將後面的字串複製到前面的字串陣列中(同樣需要前面裝得下才行)
strcpy(str, c);
printf("%s", str);
}
Hello
比較字串:
#include <stdio.h>
#include <string.h>
int main() {
char *a = "AAA", *b = "AAB";
// strcmp會比較兩個字串,並返回結果
// 把字串str1和str2從首字元開始逐個字元的進行比較
// 直到某個字元不相同或者其中一個字串比較完畢才停止比較
// 字元的比較按照ASCII碼的大小進行判斷
// 比較完成後,會返回不匹配的兩個字元的ASCII碼之差
int value = strcmp(a, b);
printf("%d", value);
}
-1
數學
接著來看用於處理數學問題的相關庫:
#include <math.h>
這裡要用到math.h
,它提供了數學計算函式,比如求算術平方根、三角函式、對數等。
#include <stdio.h>
#include <math.h>
int main() {
int a = 2;
// 使用sqrt可以求出非負數的算術平方根(底層採用牛頓逼近法計算)
double d = sqrt(a);
printf("%lf", d);
}
1.414214
能夠開根,也可以做乘方:
#include <stdio.h>
#include <math.h>
int main() {
int a = 2;
// 使用pow可以快速計算乘方,這裡求的是a的3次方
double d = pow(a, 3);
printf("%lf", d);
}
8.000000
有了這個函式,寫個水仙花數更簡單了:
#include <stdio.h>
#include <math.h>
int main() {
for (int i = 0; i < 1000; ++i) {
int a = i % 10, b = i / 10 % 10, c = i / 10 / 10;
if (pow(a, 3) + pow(b, 3) + pow(c, 3) == i) {
printf("%d 是水仙花數!\n", i);
}
}
}
0 是水仙花數!
1 是水仙花數!
153 是水仙花數!
370 是水仙花數!
371 是水仙花數!
407 是水仙花數!
當然也可以計算三角函式:
#include <math.h>
#include <stdio.h>
int main() {
// 這裡我們使用正切函式計算tan180度的值,注意要填入的是弧度值
// M_PI也是預先定義好的π的值,非常精確
printf("%f", tan(M_PI));
}
-0.000000
當然某些沒有不存在的數可能算出來會得到一個比較奇怪的結果:
#include <math.h>
#include <stdio.h>
int main() {
// 這裡計算tan90°,我們知道tan90° = sin90°/cos90° = 1/0 不存在
printf("%f", tan(M_PI / 2));
}
16331239353195370.000000
當然還有兩個比較常用的函式:
#include <stdio.h>
#include <math.h>
int main() {
double x = 3.14;
printf("不小於x的最小整數:%f\n", ceil(x));
printf("不大於x的最大整數:%f\n", floor(x));
}
不小於x的最小整數:4.000000
不大於x的最大整數:3.000000
當然也有快速求絕對值的函式:
#include <math.h>
#include <stdio.h>
int main() {
double x = -3.14;
printf("x的絕對值是:%f", fabs(x));
}
x的絕對值是:3.140000
通用
最後再來介紹一下通用工具庫stdlib
,這個庫裡面提供了大量的工具函式:
// 工具庫已經提供好了快速排序的實現函式,可以直接用
// 引數有點多,第一個是待排序陣列,第二個是待排序的數量(一開始就是陣列長度),第三個是元素大小,第四個是排序規則(提供函式實現)
// void __cdecl qsort(void *_Base,size_t _NumOfElements,size_t _SizeOfElements,int (__cdecl *_PtFuncCompare)(const void *,const void *));
在開始使用之前還要先補充一點知識,因為qsort
的原型定義,使用的是 void 型別的指標。
怎麼 void 還有指標呢?void 不是空嗎?
void 指標是一種特殊的指標,表示為“無型別指標”,由於 void 指標沒有特定的型別,因此它可以指向任何型別的資料。也就是說,任何型別的指標都可以直接賦值給 void 指標,而無需進行其他相關的強制型別轉換。
所以這裡之所以需要 void 指標,其實就是為了可以填入任何型別的陣列,而第三個引數實際上就是因為是 void 指標不知道具體給進來的型別是什麼,所以需要告訴函式使用的型別所佔大小是多少。
而最後一個引數實際上就是前面介紹的函式回撥了,因為函式不知道你的比較規則是什麼,是從小到大還是從大到小呢?所以需要編寫一個函式來對兩個待比較的元素進行大小判斷。
好了,現在瞭解了之後,就可以開始填入引數了:
#include <stdio.h>
#include <stdlib.h>
// 引數為兩個待比較的元素,返回值負數表示a比b小,正數表示a比b大,0表示相等
int compare(const void* a, const void* b) {
// 這裡因為判斷的是int所以需要先強制型別轉換為int *指標
int *x = (int*)a, *y = (int*)b;
// 其實直接返回a - b就完事了,因為如果a比b大的話算出來一定是正數,反之同理
return *x - *y;
}
int main() {
int arr[] = {5, 2, 4, 0, 7, 3, 8, 1, 9, 6};
// 工具庫已經提供好了快速排序的實現函式,可以直接用
// 引數有點多,第一個是待排序陣列,第二個是待排序的數量(一開始就是陣列長度),第三個是元素大小,第四個是排序規則(提供函式實現)
qsort(arr, 10, sizeof(int), compare);
for (int i = 0; i < 10; ++i) {
printf("%d ", arr[i]);
}
}
0 1 2 3 4 5 6 7 8 9
這樣,就可以對陣列進行快速排序了。
通用庫中還提供了exit
函式用於終止程式:
#include <stdlib.h>
int main() {
// 直接終止程式,其中引數是傳遞給父程序的(但是現在只是簡單程式)
exit(EXIT_SUCCESS);
}
不過乍一看,貌似和直接在 main 裡面 return 沒啥區別,反正都會結束。
還有兩個會在後續學習資料結構中用的較多的函式:
#include <stdio.h>
#include <stdlib.h>
int main() {
// 可以使用malloc函式來動態申請一段記憶體空間
int* p = (int*)malloc(sizeof(int));
// 申請後會返回申請到的記憶體空間的首地址
*p = 128;
printf("%d", *p);
}
128
malloc 用於向系統申請分配指定 size 個位元組的記憶體空間,返回型別是 void* 型別,如果申請成功返回首地址,如果失敗返回 NULL 空地址(比如系統記憶體不足了就可能會申請失敗)
申請到一段記憶體空間後,這段記憶體空間就可以往上面隨便讀寫資料了,實際上就是和變數一樣,只不過這個記憶體空間是自主申請的,並不是透過建立變數得到的,但是使用上其實沒啥大的區別。
不過要注意,這段記憶體使用完之後記得清理,就像函式執行完會自動銷燬其中的區域性變數一樣,如果不清理那麼這段記憶體會被一直佔用:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* p = (int*)malloc(sizeof(int));
*p = 128;
printf("%d", *p);
// 使用free函式對記憶體空間進行釋放,歸還給系統,這樣這段記憶體又可以被系統分配給別人用了
free(p);
// 指標也不能再指向那個地址了,因為它已經被釋放了
p = NULL;
}
128
記憶體資源是很寶貴的(不像硬碟幾個 T 隨便用,我們的電腦可能 32G 的記憶體都算高配了),不能隨便浪費,所以一般情況下 malloc 和 free 都是一一對應的,這樣才能安全合理地使用記憶體。
環境:
- GCC 11.4.0
- VSCode 1.93.1