函数的调用与指针初探

函数的调用与指针初探

一、背景

利用二分法求函数的根

二、代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <math.h>
#define EPS 1e-2

double f(double); //函数的声明

int main() {
double x1, x2, x0;
double fx1, fx2, fx0;
double left, right;
printf("请输入两个端点值x1,x2:\n");
scanf_s("%lf,%lf", &x1, &x2);
left = x1;
right = x2;
do {
fx1 = f(x1); //函数的调用
fx2 = f(x2);
x0 = (x1 + x2) / 2;
fx0 = f(x0);
if (fx0 * fx1 > 0) {
x1 = x0;
}
else
{
x2 = x0;
}
} while (fabs(fx0) >= EPS);
printf("在范围[%lf,%lf]中的根大约为%.3lf", left, right, x0);
}

double f(double x) { //函数的定义
double value;
value = x * x - x - 2;
return value;
}
  • 函数的声明

    提前告诉编译器 “存在这样一个函数”,并说明函数的返回值类型、名称和参数类型

    编译器先看到f()的声明,知道它是一个接收两个double、返回double的函数

  • 函数的定义

    完整描述函数的功能逻辑,包括参数的具体处理步骤和返回结果

  • 函数的调用

    在需要的地方(如main函数中)通过函数名和实际参数,触发函数定义中的逻辑执行,完成特定任务(如计算、打印、数据处理等)

三、科学计数法

在 C 语言(以及多数编程语言)中,科学计数法的写法略有简化,用 eE 代替 “×10ⁿ”,格式为:数字e指数数字E指数

举例说明:

  1. **1e-2**对应数学中的 1 × 10⁻²,即 (1 \div 10^2 = 1 \div 100 = 0.01)。
  2. **2.5e3**对应 2.5 × 10³,即 (2.5 \times 1000 = 2500)。
  3. **3e-5**对应 3 × 10⁻⁵,即 (3 \div 100000 = 0.00003)。
  4. **6.02E23**(科学常数阿伏伽德罗常数)对应 6.02 × 10²³,是一个极大的数。

核心规则:

  • 指数为 正数 时,表示乘以 10 的幂(数值变大);
  • 指数为 负数 时,表示除以 10 的幂(数值变小);
  • 尾数可以是整数(如 1e3)或小数(如 2.5e-2)。

四、备注

1.可以没有函数的声明,直接把函数的定义放到主函数的前面吗?

可以。如果将函数的定义放在主函数(main函数)之前,此时函数定义本身就兼具了 “声明” 的作用,因此可以省略单独的函数声明。

只适合函数代码比较简单的程序,否则如果头重脚轻,程序就显得不好看

2.可以将函数返回值类型改为void吗?

可以,但需要注意:void表示函数没有返回值,因此函数体内不能有return 表达式,如果改为void f(),函数的功能需要调整 —— 比如直接在函数内部打印结果,而不是通过return返回给调用者

​ 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

// 1. 函数声明(返回值类型改为void)
void multiply(int a, int b); // 无返回值,仅执行操作

int main() {
int num1 = 5, num2 = 3;

// 3. 函数调用:无需接收返回值,直接调用即可
printf("计算%d和%d的乘积:\n", num1, num2);
multiply(num1, num2); // 调用后直接在函数内打印结果

return 0;
}

// 2. 函数定义(返回值类型为void,无return语句)
void multiply(int a, int b) {
int result = a * b;
printf("乘积是:%d\n", result); // 直接在函数内输出结果
// 无需return(或可写 return; 但无实际意义)
}
3、函数定义的括号里面参数名称可以省略吗?

在函数定义中,参数名称不能省略

函数定义的核心是实现函数的具体逻辑,而参数名称是函数体内操作输入数据的 “标识”。如果省略参数名称,函数体中将无法引用这些参数,导致逻辑无法实现,编译器会直接报错。

番外

可以将中间的if判断逻辑封装到独立函数中吗

可以,代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <math.h>
#define EPS 1e-2

double f(double);
void judge(double fx0, double fx1, double *x1, double *x2, double x0);

int main() {
double x1, x2, x0;
double fx1, fx2, fx0;
double left, right;
printf("请输入两个端点值x1,x2:\n");
scanf_s("%lf,%lf", &x1, &x2);
left = x1;
right = x2;
do {
fx1 = f(x1);
fx2 = f(x2);
x0 = (x1 + x2) / 2;
fx0 = f(x0);
// 调用独立函数处理区间更新逻辑
judge(fx0, fx1, &x1, &x2, x0);
} while (fabs(fx0) >= EPS);
printf("在范围[%lf,%lf]中的根大约为%.3lf", left, right, x0);
}

// 封装区间更新的逻辑
void judge(double fx0, double fx1, double *x1, double *x2, double x0) {
if (fx0 * fx1 > 0) {
*x1 = x0; // 通过指针修改外部变量x1
} else {
*x2 = x0; // 通过指针修改外部变量x2
}
}

double f(double x) {
return x * x - x - 2; // 原函数保持不变
}

我自己写的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <math.h>
#define EPS 1e-2

double f(double);
void judge(double, double, double, double, double);

int main() {
double x1, x2, x0;
double fx1, fx2, fx0;
double left, right;
printf("请输入两个端点值x1,x2:\n");
scanf_s("%lf,%lf", &x1, &x2);
left = x1;
right = x2;
do {
fx1 = f(x1);
fx2 = f(x2);
x0 = (x1 + x2) / 2;
fx0 = f(x0);
judge(x1, x2, x0, fx1, fx0);
} while (fabs(fx0)>=EPS);
printf("在范围[%lf,%lf]中的根大约为%.3lf", left, right, x0);
}

double f(double x) {
double value;
value = x * x - x - 2;
return value;
}

void judge(double x1, double x2, double x0, double fx1, double fx0) {
if (fx0 * fx1 > 0) {
x1 = x0;
}
else
{
x2 = x0;
}
}

密码的,竟然报错了!

问了一下豆包:

你的代码中存在一个典型的 “值传递” 导致的逻辑错误,具体问题出在 judge 函数对 x1x2 的修改无法作用到 main 函数中。

问题分析:

C 语言函数参数传递默认是 值传递—— 函数接收的是实参的 “副本”(复制了一份值),而非变量本身。在你的代码中:

  • main 函数中的 x1x2 是原始变量。
  • 调用 judge(x1, x2, x0, fx1, fx0) 时,judge 函数接收的是 x1x2 的 “副本”(函数内部的局部变量 x1x2)。
  • judge 函数中修改 x1 = x0x2 = x0,只能改变这个 “副本” 的值,无法影响 main 函数中原始的 x1x2

因此,循环中 x1x2 ==始终保持初始值不变==,导致二分法无法正常缩小区间,可能陷入死循环或计算错误。

解决方案:

需要通过 指针 传递 x1x2 的地址,让 judge 函数能直接修改 main 函数中的原始变量。

问题解决:

AI在这里解释的很清楚,”仔细翻翻,看看有什么好东西”:

在 C 语言中,指针是一个非常核心的概念,它的本质是存储变量的内存地址。理解指针的作用和实现方式,能帮你搞清楚为什么之前的代码需要用指针才能正确修改main函数中的x1x2

一、指针的核心作用:间接访问和修改变量

C 语言函数参数默认是 “值传递”—— 函数内部的参数只是外部变量的副本(复制了一份值)。如果想让函数内部修改外部变量的值,就必须通过指针 “绕个弯”,直接操作变量的内存地址。

举个生活例子:

  • 假设main函数中的x1是一个放在 “房间 A” 里的变量(内存地址是0x1000,值为1.0)。
  • 如果不用指针,judge函数拿到的x1是 “房间 A” 里值的副本(放在 “房间 B”,地址0x2000,值也是1.0)。此时在judge里改x1,只是改了 “房间 B” 的值,“房间 A” 的x1完全不变。
  • 如果用指针,judge函数拿到的是 “房间 A” 的地址(0x1000)。通过这个地址,judge可以直接去 “房间 A” 里修改x1的值,main函数自然能看到变化。

二、指针的实现方式(结合你的代码)

我们通过修改后的judge函数,一步步看指针是如何工作的:

1. 指针变量的定义
1
void judge(double *x1, double *x2, double x0, double fx1, double fx0)
  • double *x1 表示:x1是一个指针变量,它专门用来存储double类型变量的内存地址。
  • * 在这里是 “指针声明符”,告诉编译器 “x1不是普通的double变量,而是指向double变量的指针”。
2. 传递变量的地址(调用函数时)
1
judge(&x1, &x2, x0, fx1, fx0);
  • &x1 中的 & 是 “取地址符”,作用是获取main函数中x1变量的内存地址(比如0x1000)。
  • 此时,judge函数的参数x1(指针变量)会接收到这个地址(0x1000),即x1指向了main中的x1变量。
3. 通过指针修改外部变量(函数内部)
1
2
3
4
5
if (fx0 * fx1 > 0) {
*x1 = x0; // 修改指针指向的变量
} else {
*x2 = x0;
}
  • *x1 中的 * 是 “解引用符”,作用是 “通过指针x1存储的地址,找到它指向的变量”(即main中的x1)。
  • *x1 = x0 等价于 “把x0的值放到指针x1指向的内存地址中”,也就是直接修改了main函数中的x1变量。

三、总结:指针如何解决你的问题

在你最初的代码中,judge函数接收的是x1x2的副本,修改副本不会影响原始变量,导致二分法无法缩小区间。

通过指针:

  1. judge函数拿到了mainx1x2的内存地址(通过&x1&x2传递);
  2. *x1*x2直接操作这个地址对应的变量,实现了 “在函数内部修改外部变量” 的效果;
  3. 最终main函数中的x1x2能正确更新,二分法正常工作。

简单说,指针就像一个 “遥控器”,拿着它(指针变量),你可以远程操作它指向的变量(不管这个变量在哪个函数里)。这也是 C 语言中实现函数间数据共享和修改的核心方式。

我说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了说白了我白说了:指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒指针就相当于一个末影盒


函数的调用与指针初探
http://example.com/2025/11/16/函数的调用与指针初探/
作者
王柏森
发布于
2025年11月16日
许可协议