【C语言】指针专题Review(Finish)
C 语言只有值传递。传指针也是传“地址值”的副本。通过指针可以改它指向的内容若要改“调用者手里的指针本身”必须传二级指针。---1. 各类指针传参速查表类型 形参写法 实参示例 函数内可改 典型用途一级指针 T *p x *p 输出参数、swap二级指针 T **p ptr *p即调用者的指针 动态分配、修改指针数组 T arr[] 或 T *arr arr arr[i] 数组遍历二维数组 T (*p)[N] 或 T p[][N] a如 int a[3][4] p[i][j] 矩阵指针数组 T *arr[] 或 T **arr strs arr[i]、*arr[i] 字符串数组函数指针 R (*fp)(Args) func 或 func 调用回调 回调、策略结构体指针 struct S *p s p-member 避免复制、修改结构体只读指针 const T *p x 不能改 *p 只读接口泛型指针 void *p 任意对象指针 需转换后使用 memcpy/qsort---2. 常见指针传参示例2.1 一级指针修改普通变量cvoid set_int(int *p) {*p 10;}int main(void) {int a 0;set_int(a); // 传地址printf(%d\n, a); // 10}2.2 二级指针修改调用者的指针c#include stdlib.hvoid alloc_int(int **p, int value) {*p (int *)malloc(sizeof(int));if (*p ! NULL) {**p value;}}int main(void) {int *p NULL;alloc_int(p, 123); // 传指针的地址printf(%d\n, *p); // 123free(p);}关键函数内 *p 才是调用者的 p。2.3 数组传参退化为指针cvoid print_arr(int arr[], int n) {// 等价于 void print_arr(int *arr, int n)for (int i 0; i n; i) {printf(%d , arr[i]);}}int main(void) {int a[5] {1, 2, 3, 4, 5};print_arr(a, 5);}注意函数内 sizeof(arr) 得到的是指针大小不是数组大小。2.4 二维数组传参指向数组的指针cvoid print_matrix(int (*m)[4], int rows) {// 也可写成 int m[][4]for (int i 0; i rows; i) {for (int j 0; j 4; j) {printf(%d , m[i][j]);}printf(\n);}}int main(void) {int a[3][4] {0};print_matrix(a, 3);}2.5 指针数组 / 字符串数组cvoid print_strs(char *strs[], int n) {// 等价于 char **strsfor (int i 0; i n; i) {printf(%s\n, strs[i]);}}int main(void) {char *arr[] {hello, world};print_strs(arr, 2);}main 的 char *argv[] 本质也是 char **argv。2.6 结构体指针cstruct Point {int x;int y;};void move_point(struct Point *p, int dx, int dy) {p-x dx;p-y dy;}void change_point_ptr(struct Point **pp, struct Point *newp) {*pp newp; // 修改调用者手里的结构体指针}2.7 const 指针参数cvoid print_int(const int *p) {// *p 10; // 错误不能改指向内容printf(%d\n, *p);}void func(int * const p) {// p NULL; // 错误p 本身不能改*p 10; // 可以改指向内容}常见含义cconst int *p; // 不能改 *pp 可改int * const p; // p 不能改*p 可改const int * const p; // 两者都不能改作为函数参数时int * const p 的顶层 const 对调用者影响不大通常写 int *p 即可。2.8 返回指针cint *find_int(int *arr, int n, int target) {for (int i 0; i n; i) {if (arr[i] target) {return arr[i];}}return NULL;}不要返回局部变量地址cint *bad(void) {int x 10;return x; // 错误x 函数结束就销毁}可以返回静态变量、全局变量、malloc 内存、传入的指针。---3. 函数声明、定义、调用3.1 函数声明声明只告诉编译器函数签名不写函数体。cint add(int a, int b);void swap(int *a, int *b);char *strdup(const char *s);void print_arr(int arr[], int n);int (*get_op(char c))(int, int); // 返回函数指针的函数参数名可省略cint add(int, int);3.2 函数定义定义包含函数体通常只能定义一次。cint add(int a, int b) {return a b;}void swap(int *a, int *b) {int t *a;*a *b;*b t;}3.3 函数调用 / 函数引用C 没有 C 那种“引用类型”。通常说的“函数引用”指函数调用或函数名作为函数指针使用。cint main(void) {int x 1, y 2;swap(x, y);int s add(x, y);return 0;}函数名在表达式中通常退化为函数指针cint (*fp)(int, int) add; // 等价于 add;int r1 fp(1, 2); // 常用int r2 (*fp)(1, 2); // 显式解引用也合法3.4 头文件声明 源文件定义math_utils.hc#ifndef MATH_UTILS_H#define MATH_UTILS_Hint add(int a, int b);void swap(int *a, int *b);#endifmath_utils.cc#include math_utils.hint add(int a, int b) {return a b;}void swap(int *a, int *b) {int t *a;*a *b;*b t;}main.cc#include stdio.h#include math_utils.hint main(void) {int a 1, b 2;swap(a, b);printf(%d\n, add(a, b));return 0;}---4. 函数指针与回调4.1 基本声明与使用cint add(int a, int b) { return a b; }int sub(int a, int b) { return a - b; }int calc(int a, int b, int (*op)(int, int)) {return op(a, b);}int main(void) {int r1 calc(3, 4, add);int r2 calc(3, 4, sub);printf(%d %d\n, r1, r2);}4.2 用 typedef 简化ctypedef int (*BinOp)(int, int);int calc(int a, int b, BinOp op) {return op(a, b);}BinOp get_op(char c) {if (c ) return add;if (c -) return sub;return NULL;}调用cBinOp op get_op();if (op ! NULL) {printf(%d\n, op(10, 20));}4.3 函数指针数组cint (*ops[2])(int, int) {add, sub};printf(%d\n, ops[0](1, 2)); // addprintf(%d\n, ops[1](1, 2)); // sub---5. 易错点总结1. 数组传参退化为指针函数内 sizeof(arr) 不是数组长度。2. 想改指针本身要传二级指针void f(int **p)。3. 不要返回局部变量地址。4. 字符串字面量只读char *s abc; s[0] A; 是未定义行为。5. 函数指针类型要匹配返回类型、参数类型必须一致。6. const 是接口约束只读参数用 const T
上一篇/下一篇内容由系统自动关联
返回资讯列表 →