指针作为参数: 传址调用
传址调用要点与 scanf 的参数
案例: 查找最大最小
const 保护参数与指针返回值
课堂编码与小测
void decompose(double x, long * int_part, double *frac_part)
{
*int_part = (long) x;
*frac_part = x - *int_part;
}
void decompose(double x, long * int_part, double *frac_part);
void decompose(double, long *, double *);
decompose(3.14159, &i, &d);
之前的 swap 演示还记得吗: 传值改不了实参。传递变量的地址而不是变量的值, 函数就能改到调用者的变量——decompose 就是靠这个思路起死回生的.
decompose的新定义:
decompose的原型可以是:
也可以是:
调用decompose:
作为调用的结果, 指向i的指针存储在int_part中, 指向d的指针存储在frac_part中:
把 x 转换为 long 类型, 存入 int_part 指向的对象中:
把 x - *int_part 的值存储到 frac_part 指向的对象中:
scanf 调用中的参数是指针——scanf 需要地址, 才能把读入的结果写回调用者的变量:
int i;
…
scanf("%d", &i);
如果没有 &, 传递给 scanf 的将是 i 的值而不是地址——scanf 无处写回结果.
int i, *p;
…
p = &i;
scanf("%d", p);
scanf("%d", &p); /*** 错误的 ***/
decompose(3.14159, i, d);
尽管scanf的参数必须是指针, 但并不总是需要&运算符:
使用&运算符是错误的:
传递指针失败可能导致灾难性后果; 缺少 & 的 decompose 调用:
当decompose在 *int_part 和 *frac_part 中存储值时, 它将修改未知的内存地址, 而不是修改 i 和 d . 若为 decompose 提供了原型, 编译器会检测到错误; scanf 的参数类型不匹配在开 -Wall 时也会被 -Wformat 检查出来——但仍不能完全依赖编译器, 传参前先想清楚类型
现场演示: 输入 10 个数, 输出其中最大和最小值——用函数完成, 且函数要带回两个结果
样例:
输入 10 个数: 3 8 1 9 2 7 4 6 5 0
最大: 9
最小: 0
⚡ 在 VS Code 中打开 minmax.c
提示: return 只能带回一个值, 两个结果怎么传出来? (想想地址的作用)
/* Finds the largest and smallest elements in an array */
#include <stdio.h>
#define N 10
void max_min(int a[], int n, int *max, int *min);
int main(void)
{
int b[N], big, small;
printf("输入 %d 个数: ", N);
for (int i = 0; i < N; i++) {
scanf("%d", &b[i]);
}
max_min(b, N, &big, &small);
printf("最大: %d\n", big);
printf("最小: %d\n", small);
return 0;
}
void max_min(int a[], int n, int *max, int *min)
{
*max = *min = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > *max) {
*max = a[i];
} else if (a[i] < *min) {
*min = a[i];
}
}
}
max_min(b, N, &big, &small)——把 big、small 的地址传给函数, 函数通过指针把结果写回去
*max = a[0] 先假设, 遍历中更大/更小就更新——指针参数让函数一次"带回"两个值
这也是 return 单值限制的经典解法
f(&x);
void f(const int *p)
{
*p = 0; /*** 错误 ***/
p = &x; /*** 合法 ***/
}
void f(int * const p)
{
*p = 0; /*** 合法 ***/
p = &x; /*** 错误 ***/
}
void f(const int * const p)
{
*p = 0; /*** 错误 ***/
p = &x; /*** 错误 ***/
}
当参数是指向变量x的指针时, 我们通常假设x将被修改:
但是, 有可能 f 只需要检查 x 的值, 而不是更改它.
使用指针的原因可能主要是因为效率: 如果变量需要大量的存储空间, 则传递变量的值会浪费时间和空间.
可以使用const来表明函数不会更改指针参数所指向的对象.
把 const 写在类型说明符之前(const int *p)表示不能通过 p 修改 *p;const 位置不同,保护的对象不同,见下列三种写法
尝试修改*p, 编译器会报错.
int *max(int *a, int *b)
{
if (*a > *b)
return a;
else
return b;
}
int *f(void)
{
int i;
…
return &i;
}
int *find_middle(int a[], int n) {
return &a[n/2];
}
允许函数返回指针:
调用max函数: p = max(&i, &j);
调用后, p 指向 i 或 j .
尽管这个例子中max返回作为参数传递给它的指针之一, 但这不是唯一的可能性. 函数还可以返回指向全局变量(文件作用域变量)或静态局部变量的指针. 永远不要返回指向自动局部变量的指针:
f返回后变量i将不存在.
指针可以指向数组元素. 如果a是一个数组, 则&a[i]是指向a中元素i的指针. 函数返回指向数组中某个元素的指针有时很有用. 下面的函数假设a有n个元素, 返回一个指向数组中间元素的指针:
现场演示
void swap(int *a, int *b), 在 main 里验证真的交换了——之前传值版的悬念今天揭晓 (样例: 交换前 x=1, y=2, 调用后 x=2, y=1)
avg 指针参数, 写 void max_min(int a[], int n, int *max, int *min, double *avg), 一次带回最大、最小和平均值
int *last_max(int *a, int n), 返回指向最后一个最大元素的指针
提示: 先想清楚"函数要改谁的什么", 再决定传什么地址
⚡ 在 VS Code 中打开 skeleton.cvoid swap(int *a, int *b) 的参数要用指针?
const int *p 与 int * const p 的区别是什么?
void f(int x, int *p) { x = 10; *p = 20; },执行 int a = 1, b = 2; f(a, &b); printf("%d %d", a, b); 输出什么?
int *f(void) { int i = 1; return &i; } 错在哪里?应该怎么改?