目录
225. 常见 math.h 数学库函数汇总
在这段代码中,你展示了如何使用标准 C 库中的一些常见数学函数,如 sqrt()、fabs()、ceil()、floor() 和 fmod()。这些函数都是在 math.h 中定义的,并且可以处理常见的数学运算。
主要函数:
sqrt(x)- 计算x的平方根。x必须是非负数。- 输入:
16.0 - 输出:
4.0(因为sqrt(16.0)等于4.0)
- 输入:
fabs(x)- 返回x的绝对值。- 输入:
-5.7 - 输出:
5.7(因为fabs(-5.7)等于5.7)
- 输入:
ceil(x)- 向上取整,返回大于或等于x的最小整数。- 输入:
2.3 - 输出:
3.0(因为ceil(2.3)等于3.0)
- 输入:
floor(x)- 向下取整,返回小于或等于x的最大整数。- 输入:
2.7 - 输出:
2.0(因为floor(2.7)等于2.0)
- 输入:
fmod(x, y)- 返回x除以y的余数。- 输入:
x = 5.5,y = 2.0 - 输出:
1.5(因为5.5 % 2.0等于1.5)
- 输入:
完整代码:
#define _USE_MATH_DEFINES
#include <stdio.h>
#include <inttypes.h>
#include <math.h>
int main(void) {
// 输入数据
double num_sqrt = 16.0; // 要求的平方根的数
double num_fabs = -5.7; // 要求的绝对值的数
double num_ceil = 2.3; // 向上取整的数
double num_floor = 2.7; // 向下取整的数
double num1_fmod = 5.5; // 求余数的被除数
double num2_fmod = 2.0; // 求余数的整数
// 计算结果
double result_sqrt = sqrt(num_sqrt);
double result_fabs = fabs(num_fabs);
double result_ceil = ceil(num_ceil);
double result_floor = floor(num_floor);
double result_fmod = fmod(num1_fmod, num2_fmod);
// 输出结果
printf("sqrt(%.2f) = %.2f\n", num_sqrt, result_sqrt);
printf("fabs(%.2f) = %.2f\n", num_fabs, result_fabs);
printf("ceil(%.2f) = %.2f\n", num_ceil, result_ceil);
printf("floor(%.2f) = %.2f\n", num_floor, result_floor);
printf("fmod(%.2f, %.2f) = %.2f\n", num1_fmod, num2_fmod, result_fmod);
return 0;
}
代码说明:
- 宏定义:
#define _USE_MATH_DEFINES使得math.h库中的常量如M_PI(圆周率)可以使用。虽然在这个示例中并未用到,但这对于涉及常量如M_PI、M_E等的应用很有用。
- 数学函数调用:
sqrt()计算平方根。fabs()计算绝对值。ceil()计算向上取整。floor()计算向下取整。fmod()计算两个数的余数。
- 格式化输出:
- 使用
%.2f来指定浮点数输出两位小数。
- 使用
示例输出:
sqrt(16.00) = 4.00
fabs(-5.70) = 5.70
ceil(2.30) = 3.00
floor(2.70) = 2.00
fmod(5.50, 2.00) = 1.50
其他常见数学函数(可供参考):
sin(x): 返回x的正弦值(x的单位是弧度)。cos(x): 返回x的余弦值。tan(x): 返回x的正切值。exp(x): 返回e^x的值(e是自然对数的底数)。log(x): 返回x的自然对数(以e为底)。pow(x, y): 返回x的y次方。
这些数学函数可以帮助你进行各种数值计算,涵盖了从简单的四则运算到更复杂的三角函数、对数函数等。
226. pow 函数示例
在这段代码中,使用了 pow 函数来计算圆的面积。pow(x, y) 用于计算 x 的 y 次方,返回 x^y 的结果。在这个例子中,pow(radius, 2) 用于计算圆的半径的平方。
代码分析:
pow(x, y):pow是math.h库中的一个数学函数,用于计算x的y次方,即x^y。- 在此示例中,
pow(radius, 2)计算半径的平方,公式为 π×r2\pi \times r^2,用于求圆的面积。
- 圆的面积计算:
- 圆的面积公式:A=π×r2A = \pi \times r^2,其中 rr 是圆的半径,π\pi 是圆周率(通过
M_PI获得)。
- 圆的面积公式:A=π×r2A = \pi \times r^2,其中 rr 是圆的半径,π\pi 是圆周率(通过
scanf_s:scanf_s是scanf的安全版本,它能防止缓冲区溢出。在这里,它被用来从标准输入读取一个double类型的圆的半径。
完整代码:
#define _USE_MATH_DEFINES
#include <stdio.h>
#include <inttypes.h>
#include <math.h>
int main(void) {
double radius = 0.0;
double circle_area = 0.0;
printf("请输入圆的半径:\n");
// 读取用户输入的半径
scanf_s("%lf", &radius);
// 计算圆的面积,公式:area = π * r^2
circle_area = M_PI * pow(radius, 2);
// 输出圆的面积
printf("圆的面积是:%.2f\n", circle_area);
return 0;
}
代码说明:
scanf_s("%lf", &radius):- 用于从标准输入读取一个
double类型的值,表示圆的半径。%lf是格式说明符,用于读取double类型的数值。
- 用于从标准输入读取一个
circle_area = M_PI \* pow(radius, 2):M_PI是math.h库中定义的圆周率常量(π),pow(radius, 2)用于计算圆的半径的平方,最后将结果乘以 π 计算圆的面积。
printf("圆的面积是:%.2f\n", circle_area):- 使用
printf输出圆的面积,%.2f格式说明符用于将浮点数输出为两位小数。
- 使用
示例输出:
请输入圆的半径:
5
圆的面积是:78.54
解释:
- 假设输入的圆的半径是 5,程序会计算出 A=π×52=3.14159×25=78.53975A = \pi \times 5^2 = 3.14159 \times 25 = 78.53975,并将结果格式化为两位小数,输出
78.54。
其他常见 pow 函数应用:
- 计算平方:
pow(x, 2)用于计算一个数的平方。 - 计算立方:
pow(x, 3)用于计算一个数的立方。 - 计算任意次方:
pow(x, y)可以计算任意的次方数,比如计算x的 5 次方,pow(x, 5)。
注意事项:
pow函数通常用于需要浮点数结果的情况,返回值为double类型。- 在一些数学运算中,
pow可能会存在精度误差,尤其是在非常大的指数运算中。
231. time.h 与时间戳的使用
在这段代码中,使用了 time.h 库来获取当前的时间戳,并将其转换为本地时间和协调世界时(UTC)。代码通过 time_t 类型获取时间戳,然后使用 localtime_s 和 gmtime_s 转换成结构体 tm,再使用 strftime 格式化输出时间。
代码分析:
- 获取当前时间戳:
time_t now = time(NULL);- 这里
time(NULL)获取当前的时间戳(即自 1970年1月1日以来的秒数)。time_t是一个数据类型,通常用于表示时间戳。
- 转换为本地时间:
struct tm local_time;localtime_s(&local_time, &now);localtime_s函数将时间戳转换为本地时间。它返回一个tm结构体,包含年、月、日、时、分、秒等字段。
- 转换为 UTC 时间:
struct tm utc_time;gmtime_s(&utc_time, &now);gmtime_s函数将时间戳转换为协调世界时(UTC)。与localtime_s类似,gmtime_s返回一个tm结构体,表示 UTC 时间。
- 时间格式化:
strftime(local_time_str, sizeof(local_time_str), "%Y-%m-%d %H:%M:%S", &local_time);strftime函数将tm结构体转换为指定格式的字符串。"%Y-%m-%d %H:%M:%S"格式表示年月日 时分秒(24小时制)。- 对本地时间和 UTC 时间分别进行格式化,并存储到
local_time_str和utc_time_str中。
- 打印结果:
- 使用
printf输出当前的时间戳、本地时间和 UTC 时间。
- 使用
完整代码:
#include <stdio.h>
#include <inttypes.h>
#include <time.h>
int main(void) {
// 获得当前的时间戳
time_t now = time(NULL);
// 转换成本地时间
struct tm local_time;
localtime_s(&local_time, &now);
// 转换为协调世界时(UTC)
struct tm utc_time;
gmtime_s(&utc_time, &now);
char local_time_str[80];
char utc_time_str[80];
// 格式化时间
strftime(local_time_str, sizeof(local_time_str), "%Y-%m-%d %H:%M:%S", &local_time);
strftime(utc_time_str, sizeof(utc_time_str), "%Y-%m-%d %H:%M:%S", &utc_time);
// 输出时间戳、本地时间和UTC时间
printf("当前时间戳:%" PRIdMAX "\n", (intmax_t)now);
printf("本地时间:%s\n", local_time_str);
printf("UTC时间:%s\n", utc_time_str);
return 0;
}
代码解释:
-
时间戳输出:
printf("当前时间戳:%" PRIdMAX "\n", (intmax_t)now);time_t类型在不同的系统上可能具有不同的大小,因此使用PRIdMAX来适配平台,确保时间戳的正确输出。
-
时间格式化:
-
strftime用于将
tm结构体转换为自定义格式的时间字符串。格式字符串
%Y-%m-%d %H:%M:%S表示:
%Y:四位年份%m:两位月份%d:两位日期%H:24小时制的小时%M:分钟%S:秒数
-
-
localtime_s和gmtime_s:- 这两个函数是线程安全的版本,分别用于获取本地时间和 UTC 时间。它们将转换后的时间存储在传入的
tm结构体中。
- 这两个函数是线程安全的版本,分别用于获取本地时间和 UTC 时间。它们将转换后的时间存储在传入的
示例输出:
当前时间戳:1640995200
本地时间:2022-01-01 00:00:00
UTC时间:2022-01-01 00:00:00
解释:
current_time是当前的时间戳,表示自 1970年1月1日以来的秒数。local_time_str表示当前本地时间,例如2022-01-01 00:00:00。utc_time_str表示当前的 UTC 时间(也可以是2022-01-01 00:00:00,如果本地时区与 UTC 相同)。
小结:
time.h提供了获取时间戳的功能,以及将时间戳转换为本地时间和 UTC 时间的函数。strftime函数使得时间格式化更加灵活,能够按需要自定义输出的时间格式。
235. malloc 函数动态内存分配的使用与释放
在这段代码中,介绍了如何使用 malloc 动态分配内存,并且演示了如何使用 free 函数释放分配的堆内存。此代码展示了静态数组与动态数组之间的区别,特别是在内存分配、生命周期、作用域和释放方面。
代码分析:
-
静态数组的声明与初始化:
int static_arr[5] = { 1,2,3,4,5 };static_arr是一个静态数组,其大小和生命周期在编译时就已经确定。数组的元素会被分配在栈上,当函数执行完毕时,这些元素会被自动销毁。
-
动态数组的声明与初始化:
int* dynamic_arr = malloc(5 * sizeof(int));malloc用于在堆上分配指定大小的内存。这里,5 * sizeof(int)表示为存储 5 个int类型数据所需的内存大小。malloc返回的是一个void*类型的指针,指向这块已分配的内存。- 如果
malloc分配内存失败,它会返回NULL,因此我们需要检查返回值。 malloc分配的内存不会自动初始化,因此需要手动填充数据。
-
动态数组的使用:
for (size_t i = 0; i < 5; i++) { dynamic_arr[i] = (i + 1) * 10; }- 在这段代码中,我们将动态数组
dynamic_arr填充为一组数据:10, 20, 30, 40, 50。
- 在这段代码中,我们将动态数组
-
打印动态数组的内容:
for (size_t i = 0; i < 5; i++) { printf("%d ", dynamic_arr[i]); }- 使用
for循环打印动态数组的内容。
- 使用
-
释放动态内存:
free(dynamic_arr);- 动态分配的内存需要使用
free显式释放,以避免内存泄漏。释放后,指针不再指向有效的内存区域,但该指针本身并未被置为NULL,这需要开发者自己注意。
- 动态分配的内存需要使用
-
错误处理:
if (dynamic_arr == NULL) { perror("动态数组分配失败"); exit(EXIT_FAILURE); }malloc如果分配失败,会返回NULL。通过检查返回值,我们可以捕获这个错误并进行适当的错误处理,例如打印错误信息并退出程序。
完整代码:
#include <stdio.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
// 静态数组的声明和初始化
// 编译时候就确定了,整个数组的大小和生命周期连同作用域,都一起决定了。
// 分配到栈上
int static_arr[5] = { 1,2,3,4,5 };
printf("静态数组的内容\n");
for (int i = 0; i < 5; i++) {
printf("%d ", static_arr[i]);
}
printf("\n");
// 动态内存分配 int arr[5]
// 分配到堆上
int* dynamic_arr = malloc(5 * sizeof(int));
if (dynamic_arr == NULL) {
perror("动态数组分配失败");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < 5; i++) {
dynamic_arr[i] = (i + 1) * 10;
}
puts("动态数组的内容:");
for (size_t i = 0; i < 5; i++) {
printf("%d ", dynamic_arr[i]);
}
// 堆内存要手动释放,因为不是简单的局部变量那么简单,局部变量会在函数执行完之后自动释放,而堆不会。
free(dynamic_arr);
return 0;
}
输出示例:
静态数组的内容
1 2 3 4 5
动态数组的内容:
10 20 30 40 50
关键点总结:
- 静态数组:
- 在编译时就确定大小,内存分配在栈上,生命周期由作用域决定,函数结束时自动销毁。
- 动态数组:
- 使用
malloc在堆上分配内存,大小在运行时决定。 - 需要显式释放内存(使用
free),否则会导致内存泄漏。
- 使用
malloc和free:malloc用于分配动态内存,free用于释放动态内存。- 使用
malloc时,必须确保分配成功,否则返回NULL。
perror:perror用于输出与errno相关的错误信息。如果malloc返回NULL,我们使用perror来输出错误信息。
可能的问题与注意事项:
- 使用
malloc分配的内存必须手动释放,否则可能导致内存泄漏。 - 在访问动态数组时,要确保内存已经分配成功。
244. Function Pointer(函数指针)的概念
在 C 语言中,函数指针是指向函数的指针。它允许你将函数作为参数传递、调用函数,或者动态地选择要执行的函数。函数指针可以用来实现回调机制、事件驱动编程等。
代码解析:
1. 函数指针声明:
int (*myFunctionPointer)(int, int);
-
这行代码声明了一个函数指针
myFunctionPointer,它指向返回类型为int、参数类型为两个int的函数。 -
语法解释:
-
int (*myFunctionPointer)(int, int);可以拆解为:
myFunctionPointer是一个指针,指向一个接受两个int参数并返回int的函数。
-
2. 指针指向函数:
myFunctionPointer = add;
myFunctionPointer被赋值为函数add的地址。此时,myFunctionPointer就指向了add函数。
3. 通过函数指针调用函数:
int result = myFunctionPointer(2, 3);
- 使用
myFunctionPointer指向的函数调用语法,调用了add(2, 3)并将结果存储在result中。 - 注意:这实际上等同于
int result = add(2, 3);,但是通过函数指针的方式调用。
4. 回调函数概念:
- 函数指针可以用于回调函数的实现。回调函数是一种机制,允许你将一个函数作为参数传递给另一个函数,然后在特定的事件或条件下被调用。
- 在这个例子中,回调机制没有完全实现,但可以理解为
myFunctionPointer代表一个可以在后续执行时调用的函数。
完整代码:
#include <stdio.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
// 函数声明
int add(int a, int b);
// 函数指针声明
int (*myFunctionPointer)(int, int);
int main(void) {
// 将函数指针指向 add 函数
myFunctionPointer = add;
// 通过指针调用函数
int result = myFunctionPointer(2, 3);
printf("The result is %d\n", result); // 输出结果:5
// 这里可以实现回调函数的概念:
// 回调函数用于事件驱动编程,可以通过传递不同的函数来实现不同的行为。
return EXIT_SUCCESS;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
输出示例:
The result is 5
解释:
myFunctionPointer是一个指向函数的指针,它指向返回int类型、接受两个int参数的函数。- 通过
myFunctionPointer = add;将add函数的地址赋值给myFunctionPointer,从而使得myFunctionPointer成为add函数的别名。 - 使用
myFunctionPointer(2, 3)调用add函数,返回结果为5。
函数指针的实际用途:
- 回调函数:在一些库或框架中,你可以通过回调函数来处理事件(例如 GUI 库、网络库等)。你将一个函数作为参数传递给其他函数,在需要的时候调用它。
- 动态选择函数:使用函数指针,你可以根据不同的条件选择执行不同的函数,这为灵活的代码实现提供了可能。
- 简化代码:通过函数指针,可以避免大量的
if或switch语句,提高代码的可读性和可扩展性。
小结:
- 函数指针 是 C 语言的一种强大功能,可以动态地指向和调用函数。
- 主要用途包括回调机制和动态函数调用。
254. 指针的作用域和生命周期
在这段代码中,我们展示了 指针的作用域 和 生命周期 如何影响指针的使用。代码包含一个局部指针 local_ptr 和一个全局指针 global_ptr,它们的作用域和生命周期不同。
代码解析:
1. 全局指针 global_ptr:
int* global_ptr;
global_ptr是一个全局指针,在整个程序中都可以访问。它的生命周期是与程序的运行时间相同的,即程序开始时分配内存,直到程序结束时释放。
2. 局部指针 local_ptr:
int* local_ptr = &local;
local_ptr是一个局部指针,它的作用域仅限于函数function内部。当function函数执行结束时,local_ptr将被销毁。
3. 动态内存分配:
global_ptr = (int*)malloc(sizeof(int));
global_ptr被分配了动态内存,指向int类型的内存空间。在function函数执行期间,global_ptr仍然有效,并且它所指向的内存区域的生命周期由malloc分配控制,直到手动调用free释放。
4. 局部指针访问:
printf("Inside function: local_ptr points to %d\n", *local_ptr);
- 这行代码访问了
local_ptr所指向的内存(即local变量)。虽然local_ptr的作用域仅限于函数内,但它在函数内部是有效的。
5. 全局指针在 main 函数中的使用:
printf("Outside function: global_ptr points to %d\n", *global_ptr);
global_ptr是一个全局指针,可以在main函数中访问并使用它所指向的内存(即动态分配的内存)。因此,尽管global_ptr在function中被赋值,但它仍然可以在main中使用。
6. 释放动态内存:
free(global_ptr);
free(global_ptr)释放了通过malloc分配的内存。因为global_ptr是一个全局指针,所以它一直有效直到被free释放。
代码实现:
#include <stdio.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
// 全局指针声明
int* global_ptr;
void function() {
int local = 5; // 局部变量
int* local_ptr = &local; // 局部指针,指向局部变量
// 动态分配内存,global_ptr 指向该内存
global_ptr = (int*)malloc(sizeof(int));
if (global_ptr != NULL) {
*global_ptr = 10; // 给分配的内存赋值
}
// 打印局部指针所指向的值
printf("Inside function: local_ptr points to %d\n", *local_ptr);
}
int main(void) {
// 调用 function
function();
// 打印 global_ptr 所指向的值
printf("Outside function: global_ptr points to %d\n", *global_ptr);
// 释放动态内存
free(global_ptr);
return EXIT_SUCCESS;
}
输出:
Inside function: local_ptr points to 5
Outside function: global_ptr points to 10
解释:
local_ptr是指向局部变量local的指针,它的作用域仅限于function函数。因此,当function执行结束时,local_ptr就会失效。global_ptr是一个全局指针,它在main函数中依然有效,指向动态分配的内存。在function中为其分配内存并赋值为10后,global_ptr在main中依然可以访问。- 使用
malloc动态分配内存时,分配的内存位于堆区,并且该内存会一直有效,直到通过free显式释放。
作用域与生命周期:
- 作用域 决定了指针变量在哪个范围内可访问。局部指针的作用域仅限于函数内部,而全局指针在整个程序中都有效。
- 生命周期 决定了指针所指向的内存或变量在内存中的存活时间。局部变量的生命周期通常与函数的执行时间相同,而全局变量的生命周期通常与程序的执行时间相同。动态分配的内存则由
malloc和free控制其生命周期。
小结:
- 局部指针 的生命周期与它所在的函数相同,出函数后被销毁。
- 全局指针 的生命周期与程序运行的生命周期相同,直到程序结束或手动释放。
- 动态内存分配 使用
malloc,生命周期由程序员通过free来管理。
274. 函数传递数组
在这段代码中,展示了如何将数组传递给函数。在 C 语言中,数组的名称本质上是指向其第一个元素的指针。因此,当你将数组传递给函数时,实际上传递的是数组的地址。
关键点分析:
- 数组作为函数参数:
void printArray(int arr[], int size)中的arr[]其实是指向整数的指针。函数接收到的是数组的首地址,而不是整个数组。sizeof(numbers) / sizeof(numbers[0])用来计算数组的大小,这样可以传递正确的数组长度。
- 传递数组到函数:
- 数组名(如
numbers)在传递给函数时会被隐式转换为指向数组首元素的指针。 - 这意味着在
printArray函数中,arr就是一个指向整数的指针,可以像普通数组一样访问。
- 数组名(如
- 两种传递数组的方式:
- 传递整个数组的首地址:
printArray(numbers, sizeof(numbers) / sizeof(numbers[0])) - 传递数组的首元素的地址(显式地使用地址运算符
&):printArray(&numbers[0], sizeof(numbers) / sizeof(numbers[0]))
- 传递整个数组的首地址:
代码解释:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <Windows.h>
#include <ctype.h>
// 函数声明
void printArray(int arr[], int size);
int main(void) {
// 数组声明和初始化
int numbers[] = { 1,2,3,4,5 };
int numbers_2[] = { 2,3,4,5,6 };
int numbers_3[] = { 32,3,4,5,6 };
// 传递数组给 printArray 函数
printArray(numbers, sizeof(numbers) / sizeof(numbers[0]));
printArray(&numbers[0], sizeof(numbers) / sizeof(numbers[0]));
printArray(numbers_2, sizeof(numbers_2) / sizeof(numbers_2[0]));
printArray(numbers_3, sizeof(numbers_3) / sizeof(numbers_3[0]));
}
// 打印数组的函数定义
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]); // 打印数组的每个元素
}
printf("\n");
}
代码运行步骤:
-
数组声明与初始化:
int numbers[] = { 1,2,3,4,5 }; int numbers_2[] = { 2,3,4,5,6 }; int numbers_3[] = { 32,3,4,5,6 }; -
传递数组到
printArray函数:printArray(numbers, sizeof(numbers) / sizeof(numbers[0])); printArray(&numbers[0], sizeof(numbers) / sizeof(numbers[0]));sizeof(numbers) / sizeof(numbers[0])用来计算数组的长度。这里的sizeof(numbers)返回整个数组的大小,sizeof(numbers[0])返回数组元素的大小,因此两者相除即可得到数组的元素数量。- 通过
numbers或&numbers[0]传递数组到printArray函数,实际上都传递了数组的首地址。
-
printArray函数:- 在
printArray函数中,接收到的是指向数组的指针arr[],然后通过遍历arr[],打印每个元素。
- 在
输出:
1 2 3 4 5
1 2 3 4 5
2 3 4 5 6
32 3 4 5 6
总结:
- 在 C 语言中,数组作为参数传递给函数时,实际上是传递了数组的首地址。你可以通过数组名或首元素的地址显式传递。
- 无论是传递
numbers还是&numbers[0],都能正确地传递数组的地址到printArray函数,并且通过数组指针访问每个元素。 - 数组的大小通过
sizeof(numbers) / sizeof(numbers[0])计算得到,确保在传递数组时能够正确获取数组的长度。
注意事项:
- 如果数组是通过指针传递给函数的,必须确保在函数内部传递正确的数组大小,以便正确处理数组元素。
474

被折叠的 条评论
为什么被折叠?



