函数指针与指针运算

文章目录

1. 多级指针

c 复制代码
#include<stdio.h>
int main() {
	int num = 999;
	int* num_p = &num;
	int** num_p_p = &num_p;
	int*** num_ppp = &num_p_p;
	printf("num_p的地址%p , num_p的值是%d\n", num_p, *num_p);
	printf("num_p_p的地址%p , num_p_p的值是%d\n", num_p_p, **num_p_p);
	printf("num_ppp的地址%p , num_ppp的值是%d\n", num_ppp, ***num_ppp);
	return 0;

}
bash 复制代码
num_p的地址000000000062FE14 , num_p的值是999
num_p_p的地址000000000062FE08 , num_p_p的值是999
num_ppp的地址000000000062FE00 , num_ppp的值是999

展示了一级、二级、三级指针的定义与解引用

***num_ppp 最终得到 num 的值(999)。

2.数组与数组指针

c 复制代码
#include <stdio.h>

// 数组与数组指针。
int main() {
    // 定义数组
    // int [] arr = {1,2,3,4}; 错误的写法
    int arr[]  = {1,2,3,4};
    int i = 0;
    for (i = 0; i < 4; ++i) {
        printf("%d\n", arr[i]); // 取值
    }

    // 数组 和 指针 挂钩
    // 数组的内存地址 == 第一个元素的内存地址  == &arr
    // 数组的内存地址 == 第一个元素,不是第二个元素,也不是第n个元素
    printf("arr  =   %d\n", arr);
    printf("&arr  =   %d\n", &arr);
    printf("&arr[0]  =   %d\n", &arr[0]);

    // 既然数组就是一个内存地址
    int * arr_p = arr;

    printf("%d\n", *arr_p); // *arr_p 取出元素一内存地址的值 1

    arr_p ++; // 指针挪动   元素二的内存地址了

    printf("%d\n", *arr_p); // *arr_p 取出元素二内存地址的值 2

    arr_p += 2;

    printf("%d\n", *arr_p); // 输出4

    // 输出1

    arr_p -= 3; // 挪动指针指向到 元素一

    printf("%d\n", *arr_p);

    arr_p += 2000;

    printf("%d\n", *arr_p); // 系统值 572662306


    // 以后:我想 三维数组/三级指针
    // int *** arrPpp;

    return 0;
}
bash 复制代码
1
2
3
4
6422256
6422256
6422256
1
2
4
1
指针类型 解引用大小 p + 1 偏移
char* 1 字节 +1
int* 4 字节 +4
double* 8 字节 +8

3.采用指针遍历数组

c 复制代码
#include <stdio.h>

// 3.采用指针遍历数组。

int main() {

    // 定义数组
    // int [] arr = {1,2,3,4}; 错误的写法
    int arr[]  = {1,2,3,4};
    printf("%d\n", (*(&arr))[0]);  // ✅ 输出 1
    // 数组是连续的内存空间(没有断层,有规律) 数组  每次挪动 4个字节 == int数组

    int * arr_p = arr;

    int i = 0;
    for (i = 0; i < 4; ++i) {
        printf("位置%d的值是:%d\n", i, * (arr_p + i));

        // 04    08   12    16
        printf("位置%d的内存地址是:%p\n", i, (arr_p + i));
    }

    return 0;
}
bash 复制代码
1
位置0的值是:1
位置0的内存地址是:000000000062FE00
位置1的值是:2
位置1的内存地址是:000000000062FE04
位置2的值是:3
位置2的内存地址是:000000000062FE08
位置3的值是:4
位置3的内存地址是:000000000062FE0C
表达式 实际含义 能否用 %d 打印值?
arr[0] 第一个元素的值 ✅ 可以
*arr 等价于 arr[0] ✅ 可以
*(&arr) 整个数组(类型 int[4]),传参时退化为 int* ❌ 不能!会打印地址
(*(&arr))[0] 先取数组,再取第 0 个元素 ✅ 可以

4.循环时给数组赋值

c 复制代码
int main() {

	int arr[4];
	int* arrP = arr;
	int j = 0;
	//循环给数组赋值
	for (j = 0; j < 4; ++j) {
		*(arrP + j) = (j + 10001);
	}

	printf("sizeof arr %d\n size(int) %d\n", sizeof arr, sizeof(int));//sizeof arr(int占4个字节,一个数组占4个 4*4 = 16 )
	for (int i = 0; i < sizeof arr / sizeof(int); ++i) {
		printf("位置%d的值是:%d\n", i, *(arrP + i));
	}

	return 0;
}
bash 复制代码
sizeof arr 16
 size(int) 4
位置0的值是:10001
位置1的值是:10002
位置2的值是:10003
位置3的值是:10004

sizeof arr = 4 个 int × 4 字节 = 16 字节

sizeof(int) 通常是 4 字节(几乎所有现代平台)

因此 sizeof arr / sizeof(int) = 4,循环正好遍历全部元素

5.数组指针操作的几种方式

c 复制代码
#include <stdio.h>

// 5.数组指针操作的几种方式。
int main() {

    int arr[]  = {1,9,0,9999};

    int * arrP = arr;

    for (int i = 0; i < 4; ++i) {
        printf("%d\n", arrP[i]);
        printf("%d\n", *(arrP + i)); //挪动元素的内存地址,再取内存地址的值
        printf("%d\n", *arrP + i);  //取元素第一个的内存地址的值加上i的值
    }

    return 0;
}
bash 复制代码
1
1
1
9
9
2
0
0
3
9999
9999
4

当 i = 0:

c 复制代码
1     // arrP[0]
1     // *(arrP + 0)
1     // *arrP + 0 → 1 + 0 = 1

当 i = 1:

c 复制代码
9     // arrP[1]
9     // *(arrP + 1)
2     // *arrP + 1 → 1 + 1 = 2 ❗

当 i = 2:

c 复制代码
0     // arrP[2]
0     // *(arrP + 2)
3     // *arrP + 2 → 1 + 2 = 3 ❗

当 i = 3:

c 复制代码
9999  // arrP[3]
9999  // *(arrP + 3)
4     // *arrP + 3 → 1 + 3 = 4 ❗

6.指针类型有何用?

c 复制代码
#include <stdio.h>

// 6.指针类型有何用?。
int main() {

    int num = 12;

    int * num_p = &num;

    // 优化处理,报错:检测无法通过的
    double * num_p_d = num_p;

    printf("%d\n", sizeof num_p);
    printf("%d\n", sizeof num_p_d);

    // 指针占用的内存大小是?  int double xxx 的指针 永远都是   4个字节(32位)    4*2(64位)

    // 6.指针类型有何用?。  既然都是 4个字节,为什么还要分 指针类型
    // 答:取值的时候,怎么取,这就是类型规定的好处

    // int * p; // 类型是为了计算偏移量

    char c = 'a';
    // char * p = &c;
    // char * p = "AAAA";

    return 0;
}

7.函数指针

c 复制代码
#include <stdio.h>

void add(int num1, int num2); // 先声明

void mins(int num1, int num2) {
    printf("num1 - num2 = %d\n", (num1 - num2));
}

// 操作 回调到  add  mins
// void(*method)(int,int)  声明好 函数指针
// void 返回值
// (*method) 函数名
// (int,int) 两个参数
void opreate(void(*method)(int,int), int num1, int num2) {
    method(num1, num2);

    printf("opreate函数的 method指针是多少:%p\n", method);
}

// 7.函数指针。(回调)  Java接口的回调
int main() {  // 【第一种写法】

    opreate(add,  10, 10);
    opreate(mins,  100, 10);

    // 原理是什么?

    printf("main函数的 add指针是多少:%p\n", add);
    printf("main函数的 mins指针是多少:%p\n", mins);

    // &add和add是一样的值吗
    printf("%p, %p\n", add, &add); //  004018CE, 004018CE  一样的

    return 0;
}

// 再实现 使用
void add(int num1, int num2) {
    printf("num1 + num2 = %d\n", (num1 + num2));
}
bash 复制代码
num1 + num2 = 20
opreate函数的 method指针是多少:0000000000401615
num1 - num2 = 90
opreate函数的 method指针是多少:0000000000401530
main函数的 add指针是多少:0000000000401615
main函数的 mins指针是多少:0000000000401530
0000000000401615, 0000000000401615
c 复制代码
#include <stdio.h>

void callBackMethod(char * fileName, int current, int total) {
    printf("%s图片压缩的进度是:%d/%d\n", fileName, current, total);
}

// 压缩的方法
// 定义函数指针: 返回值(*名称)(int,double)
void compress(char * fileName, void(*callBackP)(char *,int,int)) {
    callBackP(fileName, 5, 100); // 回调给外交 压缩的进度情况
}

// 函数指针2
int main() {

    // 1 如果有问题
    // VS  Clion 通过了,  Linux 可能不通过(因为这样不合规范)
    // void (* call) (char *, int ,int) = callBackMethod;

    // 有种temp的思路一样的感觉
    //  2 再换成这种方式 【第二种写法】
    // Linux  先定义, 再赋值
    void (* call) (char *, int ,int);
    call = &callBackMethod;

    void (* call2) (char *, int ,int);
    void (* call3) (char *, int ,int);
    void (* call4) (char *, int ,int);
    void (* call5) (char *, int ,int);
    call5 = &callBackMethod; // &callBackMethod 1000H  == callBackMethod 1000H

    compress("cyr.png", call5);

    // 常量指针  指针常量   【函数指针】

    // 字符串  操作符重载 + C函数
    // char * c = "Derry" + "A";
}
bash 复制代码
cyr.png图片压缩的进度是:5/100
c 复制代码
#include <stdio.h>

void add(int i, int j) {
    printf("i+j=%d\n", i + j);
}

void add2(int i, int j) {
    printf("2 i+j=%d\n", i + j);
}

// 函数 函数指针声明来接收函数
void test(void(*p)(int, int)) {
    p(9, 9); // 上节课 省略*

    (*p)(9, 9);
    (p)(9, 9); // 省略*

    // (&p)(9, 9); 在源码没有看到这种写法

    // 思考:p 为什么也可以不用写 *   函数的上面已经声明就是 函数指针,所以可以省略*

}

int main() {
    test(add);
    test(add2);
    return 0;
}

为什么 p(9,9) 不用 *?

C 语言允许函数指针直接调用,是语法糖

p 和 *p 在调用时等价吗?

✅ 完全等价

&p 能调用吗?

❌ 不能,&p 是二级指针

相关推荐
敲皮裤的代码6 小时前
《C语言》深入理解指针(4)
c语言
J-TS6 小时前
线性自抗扰控制LADRC
c语言·人工智能·stm32·单片机·算法
A9better7 小时前
C++——指针与内存
c语言·开发语言·c++·学习
坚持编程的菜鸟7 小时前
互质数的个数
c语言·算法
浅念-8 小时前
C++ 模板初阶:从泛型编程到函数模板与类模板
c语言·开发语言·数据结构·c++·笔记·学习
DevilSeagull8 小时前
C语言: C语言内存函数详解
c语言·开发语言·算法
你怎么知道我是队长10 小时前
C语言---排序算法8---递归快速排序法
c语言·算法·排序算法
白太岁10 小时前
操作系统开发:(8) 任务/线程的创建、调度与管理(实现 tasks.h 与 tasks.c)
c语言·开发语言·bash
cameron_tt11 小时前
定时器中断应用 HC-SR04超声波测距模块、定时器输出PWM应用 控制SG90舵机
c语言·嵌入式硬件