C语言 16 系统库
前面了解了如何使用#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