文章目录
-
- [1. 多级指针](#1. 多级指针)
- 2.数组与数组指针
- 3.采用指针遍历数组
- 4.循环时给数组赋值
- 5.数组指针操作的几种方式
- 6.指针类型有何用?
- 7.函数指针
1. 多级指针
c
#include<stdio.h>
int main() {
int num = 999;
int* num_p = #
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 = #
// 优化处理,报错:检测无法通过的
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 是二级指针