目录
## 概述
## 指针
### 指针特点
## 指针变量
### 指针变量特点
## 区别
## 指针变量的使用
定义指针变量时:
使用指针变量时:
## 通过指针间接修改变量的值
## 指针大小
指针大小与数据类型无关:无论指针指向什么类型的数据(int、char、double等),指针本身的大小只取决于系统的位数(32位或64位)。
## 指针步长
### 指针步长的计算方式
## 空指针和野指针
## 多级指针
## 指针与常量
## 函数参数传递内容
1、参数传值
2、参数传址
## 函数指针
1、函数名
2、函数指针
3、回调函数
内存:房间
地址:房间号
int*
表示指向int
类型数据的指针,char*
表示指向char
类型数据的指针int a = 10;
int* p = &a; // p是一个指针,存储变量a的地址
NULL
或nullptr
),表示它不指向任何有效的内存地址。int a = 10;
int* p = &a; // p是一个指针变量,存储变量a的地址
int** q = p; // q也是一个指针变量,它存储了p的值(即a的地址)
#include
int main() {
int a = 10; // a是一个整型变量,值为10
int* p = &a; // p是一个指针变量,存储变量a的地址
int** q = &p; // q是一个指针变量,存储变量p的地址
printf("a的值:%d\n", a); // 输出a的值
printf("a的地址:%p\n", &a); // 输出a的地址
printf("p的值:%p\n", p); // 输出p的值(即a的地址)
printf("p的地址:%p\n", &p); // 输出p的地址
printf("q的值:%p\n", q); // 输出q的值(即p的地址)
printf("q的地址:%p\n", &q); // 输出q的地址
printf("通过p访问a的值:%d\n", *p); // 通过指针变量p访问a的值
printf("通过q访问p的值:%p\n", *q); // 通过指针变量q访问p的值
printf("通过q访问a的值:%d\n", **q); // 通过指针变量q访问a的值
return 0;
}
类型 变量;
类型 * 指针变量 = &变量;
& 取地址:返回操作数内存地址
*
表示所声明变量为指针类型 【指针变量的类型比这个变量的类型多一个*】
*
叫解引用,可以操作(读取、修改)指针所指向的变量的值*
号可以用来操作指针所指向的内存空间#include
int main() {
// 定义一个int类型的变量,同时赋值为10
int a = 10;
// 打印变量的地址
printf("&a = %p\n", &a);
// 定义一个指针变量,int *保存int的地址
// int *代表是一种数据类型,int *指针类型,p才是变量名
int* p;
// 指针指向谁,就把谁的地址赋值给这个指针变量
p = &a;
// 打印p, *p, p指向了a的地址,*p就是a的值
printf("p = %p, *p = %d\n", p, *p);
return 0;
}
#include
int main() {
// 定义一个int类型变量a,同时赋值为0
int a = 0;
// 定义int *指针变量,同时赋值a的地址
int *p = &a;
// 通过指针间接修改a的值
*p = 123;
printf("a = %d\n", a);
// 定义一个int类型变量b,同时赋值为5
int b = 5;
// p 保存 b的地址
p = &b;
// 通过指针间接修改b的值
*p = 250;
printf("b = %d\n", b);
return 0;
}
int
、char
、double
等),指针本身的大小只取决于系统的位数(32位或64位)。指针大小与系统架构相关:
在32位系统上,指针大小为4字节。
在64位系统上,指针大小为8字节。
使用sizeof
运算符:可以通过sizeof
运算符来动态获取指针的大小,这在编写跨平台代码时非常有用。
指针类型与数据类型的关系:虽然指针的大小与指向的数据类型无关,但在使用指针时,仍然需要确保指针类型与它所指向的数据类型匹配,以避免类型不匹配错误。
是指指针在进行加1或减1操作时,其地址值的变化量。指针步长的大小并不是固定的,而是与指针所指向的数据类型大小有关。
当对指针进行加1操作时,指针会指向下一个相同类型的数据。因此,指针步长等于它所指向的数据类型的大小。具体来说:
如果指针指向char
类型,步长为1字节(因为sizeof(char)
为1字节)。
如果指针指向int
类型,步长为sizeof(int)
字节(通常是4字节)。
如果指针指向double
类型,步长为sizeof(double)
字节(通常是8字节)。
#include
int main() {
char charArr[] = "Hello";
int intArr[] = {1, 2, 3, 4, 5};
double doubleArr[] = {1.1, 2.2, 3.3, 4.4, 5.5};
char* charPtr = charArr;
int* intPtr = intArr;
double* doublePtr = doubleArr;
printf("Initial char pointer: %p\n", charPtr);
printf("Initial int pointer: %p\n", intPtr);
printf("Initial double pointer: %p\n", doublePtr);
charPtr++;
intPtr++;
doublePtr++;
printf("After increment:\n");
printf("Char pointer: %p (Step size: %zu bytes)\n", charPtr, sizeof(char));
printf("Int pointer: %p (Step size: %zu bytes)\n", intPtr, sizeof(int));
printf("Double pointer: %p (Step size: %zu bytes)\n", doublePtr, sizeof(double));
return 0;
}
结果:
#include
int main() {
int *p;
p = 0x12345678; // 给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义
// *p = 1000; // 操作野指针指向未知区域,内存出问题,err
printf("111111111111111111\n");
int *q = NULL; // 空指针
return 0;
}
Initial char pointer: 0x7ffeea7b1000
Initial int pointer: 0x7ffeea7b1010
Initial double pointer: 0x7ffeea7b1030
After increment:
Char pointer: 0x7ffeea7b1001 (Step size: 1 bytes)
Int pointer: 0x7ffeea7b1014 (Step size: 4 bytes)
Double pointer: 0x7ffeea7b1038 (Step size: 8 bytes)
野指针不会直接引发错误,操作野指针指向的内存区域才会出问题
#include
int main() {
int *p;
p = 0x12345678; // 给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义
// *p = 1000; // 操作野指针指向未知区域,内存出问题,err
printf("111111111111111111\n");
int *q = NULL; // 空指针
return 0;
}
#include
int main() {
int a = 100;
// 一级指针
int* p1 = &a;
printf("&a=%p\n", &a);
printf("p1=%p\n", p1);
printf("&p1=%p\n", &p1);
// 二级指针,可以存储一级指针变量的地址
int** p2 = &p1;
printf("p2=%p\n", p2);
printf("&p2=%p\n", &p2);
// 三级指针,可以存储二级指针变量的地址
int*** p3 = &p2;
printf("p3=%p\n", p3);
printf("&p3=%p\n", &p3);
printf("---------------------\n");
// 通过一级指针访问100,打印出来
printf("*p1=%d\n", *p1);
// 通过二级指针访问100,打印出来
printf("**p2=%d\n", **p2);
// 通过三级指针访问100,打印出来
printf("***p3=%d\n", ***p3);
return 0;
}
#include
int main() {
int a = 1;
int b = 2;
// p1 可以改,*p1不能改
const int *p1 = &a; // 等价于 int const *p1 = &a;
// p1 = &b; // ok
// *p1 = 555; // err
// p2 不能修改,*p2可以修改
int *const p2 = &a;
// p2 = &b; //err
// *p2 = 555; // ok
// p3 和 *p 都不能改
const int *const p3 = &a;
// p3 = &b; // err
// *p3 = 555; // err
return 0;
}
记忆技巧:从左往右看,跳过类型,看修饰哪个字符
*p
, 说明指针指向的变量内容不能改变(即:值不能改)p
,说明指针的指向不能改变 (即:地址不能改)// 函数参数传值,函数内部交换2个变量的值,验证函数外部的变量有没有改变
#include
// 函数定义
void func(int m, int n) {
// 函数内部交换2个变量的值
int temp = m;
m = n;
n = temp;
printf("函数内部 m = %d, n = %d\n", m, n);
}
int main() {
int a = 10;
int b = 20;
// 调用函数,值传递
func(a, b);
printf("函数外部 a = %d, b = %d\n", a, b);
return 0;
}
// 函数参数传地址,函数内部交换2个指针指向内存的值,验证函数外部的变量有没有改变
#include
// 函数定义
void func(int *m, int *n) {
// 函数内部交换2个指针指向内存的值
int temp = *m;
*m = *n;
*n = temp;
printf("函数内部 *m = %d, *n = %d\n", *m, *n);
}
int main() {
int a = 10;
int b = 20;
// 调用函数,地址传递
func(&a, &b);
printf("函数外部 a = %d, b = %d\n", a, b);
return 0;
}
返回值 (*函数指针变量)(形参列表);
//函数指针变量的定义,其中返回值、形参列表需要和指向的函数匹配
#include
void func(int a) {
printf("a = %d\n", a);
}
int main() {
// 函数指针变量的定义,同时初始化
void (*p1)(int a) = func;
// 通过函数指针变量调用函数
p1(10);
// 先定义函数指针变量,后面再赋值
void (*p2)(int);
p2 = func;
// 通过函数指针变量调用函数
p2(20);
return 0;
}
#include
// 定义函数,函数指针做形参
int calc(int a, int b, int (*p)(int, int)){
// 通过函数指针变量调用函数,获取返回值
int res = p(a, b);
return res;
}
// 定义加法函数
int add(int x, int y) {
return x + y;
}
// 定义减法函数
int sub(int x, int y) {
return x - y;
}
int main() {
int result;
// 回调加法函数
result = calc(1, 2, add);
printf("result = %d\n", result);
// 回调减法函数
result = calc(10, 5, sub);
printf("result = %d\n", result);
return 0;
}