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
posted @ 2024-09-27 23:27  天航星  阅读(5)  评论(0编辑  收藏  举报