目录
一、指针变量
指针就是地址,口语中说的指针通常指的是指针变量
我们通过 & (取地址操作符)取出一个变量的内存地址,把地址存放到一个变量里面,那这个变量就是指针变量
int main()
{
int q1=10;
int* qi=&q1;
//此时这个qi就是一个指针变量
printf("%p\n",qi);
}
二、指针和指针类型
1、指针类型
指针类型由两部分组成分别为 数据类型 与 *,比如( int* pi)
* 说明pi是一个指针变量,int 说明 pi 指向的对象为 int 类型
char* pc=null;//字符指针
// * 说明pc是一个指针变量,char 说明 pc 指向的对象为 char 类型
int* pi=null;//整型指针
// * 说明pi是一个指针变量,int 说明 pi 指向的对象为 int 类型
short* ps=null;//短整型指针
long* pl=null;//长整型指针
float* pf=null;//单精度浮点型指针
double* pd=null;//双精度浮点型指针
2、指针变量大小
指针变量本质上来说就是存放地址的,地址的存放需要多少大小,指针变量就有多大
32位机器上,有32根地址线,一个地址为32个二进制位,需要4个字节
比如在32为机器上,double*、int*、char*、short*等都是4个字节大小
64位机器上,有64根地址线,一个地址为64个二进制位,需要8个字节
比如在64为机器上,double*、int*、char*、short*等都是8个字节大小
int main()
{
int num = 1;
//通过 & 操作符取出变量的地址存放到指针变量中
int* pi = #
//pi就是一个指针变量
//32位机器上,有32根地址线,一个地址为32个二进制位,需要4个字节
//64位机器上,有64根地址线,一个地址为64个二进制位,需要8个字节
printf("%d\n", sizeof(pi));// 8
return 0;
}
三、指针类型的意义
指针类型决定了,在对指针变量进行解引用操作时,一次可以操作多少个字节
1、解引用
int main()
{
int num = 0x11111111;
//通过 & 操作符取出变量的地址存放到指针变量中
int* pi = #
//pi就是一个指针变量
*pi = 0;
printf("%d\n", *pi);//输出0
return 0;
}
可以看到在进行解引用操作时,内存中修改了4个字节,,因为一个int类型就占4个字节
int main()
{
int num = 0x11111111;
//通过 & 操作符取出变量的地址存放到指针变量中
char* pi = (char*) & num;
//pi就是一个指针变量
*pi = 0;
printf("%d\n", num);//不是0
return 0;
}
可以看到在进行解引用操作时,内存中修改了1个字节,,因为一个char类型就占1个字节
2、指针+-整数
指针类型决定了指针在运算时访问的字节大小(走一步多大距离)
1、char*指针+1,将访问1个字节,也就是先后走4个字节
2、int*指针+1,将访问4个字节,也就是先后走4个字节
3、double*指针+1,将访问8个字节,也就是先后走8个字节
...................
//演示实例
#include <stdio.h>
int main()
{
int n = 10;
char *pc = (char*)&n;
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}
3、void* 指针类型
void 指针类型可以接受任意类型的指针类型,但是不能直接对void* 类型的指针进行指针+-与解引用操作,必须通过强制类型转换后才能进行指针+-与解引用操作
int main()
{
int num = 10;
//通过 & 操作符取出变量的地址存放到指针变量中
void* pi = #
//pi就是一个指针变量
//不能直接对void* 类型的指针进行指针+-与解引用操作
*pi = 0;// error
pi + 1;// error
//必须通过强制类型转换后才能进行指针+-与解引用操作
*(int*)pi = 1;
printf("%d\n", num);
return 0;
}
四、野指针
指针指向的位置是不可知的,随机的等不明确的被称为野指针
1、指针未初始化
2、指针越界访问
3、指针指向的空间已经释放(局部变量在离开作用域时会被销毁)
................
#include <stdio.h>
int main()
{
(1)指针未初始化
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;// error
return 0;
(2)指针越界访问
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
(3)指针指向的空间已经释放
int test()
{
int q1=1;// 局部变量在离开作用域时会被销毁
return &q1;
}
int* p = test();
*p=200;// error
return 0;
}
如何规避野指针
- 指针初始化(已知指向时明确初始化;未知初始化为NULL)
- 小心指针越界
- 指针指向空间释放,及时置NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
五、指针的运算
1、指针 +- 整数
指针类型决定了指针+-整数在时访问的字节大小(走一步多大距离)
1、char*指针+n,将访问1个字节,也就是向后走1*n个字节
2、int*指针+n,将访问4个字节,也就是向后走4*n个字节
3、double*指针+n,将访问个字节,也就是向后走8*n个字节
...................
int main()
{
// 数组在内存中是连续存储的,当我们知道首元素地址时,就可以找到所有元素
const int len = 10;
int arr[len];
for (int i = 0; i < len; i++)
{
arr[i] = i + 1;
}
int* p = &arr[0];
// int*指针+n,将访问4个字节,向后走4*n个字节
for (int i = 0; i < len; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
2、指针 -+ 指针
1、两个指针相减的前提是:指针指向的是同一块连续的空间
2、指针和指针相减的绝对值等于指针和指针之间的元素个数
3、指针+指针没有意义
#include<stdio.h>
int main()
{
int arr[10] = {0};
//指针和指针相减的绝对值等于指针和指针之间的元素个数
printf("%d\n", &arr[9] - &arr[0]);//9
printf("%d\n", &arr[0] - &arr[9]);//-9
//两个指针相减的前提是:指针指向的同一块连续的空间
int arr2[10] = {0};
printf("%d\n", &arr[9] - &arr2[0]);//err
return 0;
}
六、二级指针
指针变量也是变量,是变量就有地址,用来存放指针变量的指针就是二级指针
int * p -----> * 表示 p 是一个指针变量,而 int 是 p 所指向地址的类型
int* * p -----> * 表示 p 是一个指针变量,而 int* 是 p 所指向地址的类型
int main()
{
int a = 10;//
int *pa = &a;//pa就是指针变量,一级指针变量,表示指针指向的a是int
int* *ppa = &pa;//ppa就二级指针,表示pp指向的p的类型是int*
return 0;
//理解 int * p -----> * 表示 p 是一个指针变量,而 int 是 p 所指向地址的类型
// int* * p -----> * 表示 p 是一个指针变量,而 int* 是 p 所指向地址的类型
}
七、指针数组
指针数组是用来存放指针的数组
int main()
{
//整型数组-存放整型的数组
int arr[10];
//字符数组-存放字符的数组
char arr2[5];
//指针数组-存放指针的数组
int* arr3[5];//存放整型指针的数组
char* arr4[6];//存放字符指针的数组
return 0;
}
利用指针数组将多个一维数组串联为一个二维数组
int main()
{
//用一维数组模拟一个二维数组
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int arr4[] = { 4,5,6,7,8 };
int* arr[4] = {arr1, arr2, arr3, arr4};//用指针数组管理一维数组
int i = 0;
for (i = 0; i < 4; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
//1,2,3,4,5
//2,3,4,5,6
//3,4,5,6,7
//4,5,6,7,8
return 0;
}
八、数组指针
定义:type (*name) [num]
数组指针是用来存放数组的地址
数组指针在进行+-整数时,一次将跳过整个数组的大小
数组也是一种类型,因此在创建数组指针时要明确数组的类型也就是数组的大小
int main()
{
int arr[10];
//数组也是一种类型
//因此在创建数组指针时要明确数组的类型也就是数组的大小
int(*p)[10]=&arr;
//p先与*结合,说p是一个指针变量
//指针指向的是一个大小为10的整形数组,叫数组指针变量(存放数组的地址)
return 0;
}
九、函数指针
定义:int (*name) (形参,形参.......)
函数指针用来存放函数的地址
对于函数而言,&函数名等价于函数名,这有区别于数组
函数指针可以直接使用,不需要解引用
void print(int a, int b, int c)
{
printf("%d %d %d\n", a, b, c);
}
int main()
{
//对于函数而言,&函数名等价于函数名,这有区别于数组
void (*p)(int, int, int) = &print;
void (*p)(int, int, int) = print;
(*p)(1, 2, 3);
//函数指针可以直接使用,不需要解引用
p(1, 2, 3);
// 1 2 3
return 0;
}
十、函数指针数组
定义:int (*name [数量] ) (形参,形参.......)
函数指针数组用来存放函数指针的数组
实例:
利用函数指针实现计数器
// 执行加法
int Add(int x, int y)
{
return x + y;
}
// 执行减法
int Sub(int x, int y)
{
return x - y;
}
// 执行乘法
int Mul(int x, int y)
{
return x * y;
}
// 执行除法
int Div(int x, int y)
{
return x / y;
}
// 菜单
void menu()
{
printf("****************\n");
printf("* 1.Add 2.Sub *\n");
printf("* 3.Mul 4.Div *\n");
printf("*** 0.exit ***\n");
printf("****************\n");
}
int main()
{
menu();
int input = 0;
printf("请选择:");
scanf("%d", &input);
int ret = 0;
//转移表
//创建函数指针数组,依次放入函数
// 1——加法
// 2——减法
// 3——乘法
// 4——除法
int(*pfarr[])(int, int) = { 0,Add,Sub,Mul,Div };
do
{
// 判断是否合法
if (input == 0)
{
printf("退出\n");
break;
}
else if (input >= 1 && input <= 4)
{
int x = 0;
int y = 0;
printf("请输入两个操作数:");
scanf("%d%d", &x, &y);
// 返回选择对应运算的结果
ret = pfarr[input](x, y);
printf("结果是%d\n", ret);
break;
}
else
{
printf("选择错误!");
}
} while (input);
return 0;
}
标签:变量,int,C语言,数组,printf,全解,pi,指针 From: https://blog.csdn.net/LVZHUO_2022/article/details/142858841