目录
一:指针是什么?
二:指针和指针类型
2.1 指针
2.2 指针类型
2.3 指针+-整数
2.4 指针的解引用
三:野指针
3.1野指针成因
3.1-1 指针未初始化
3.1-2 指针越界访问
3.1-3 指针指向的空间释放
3.2如何规避野指针
四:指针运算
4.1指针+-整数
4.2指针-指针
4.3指针的关系运算
五:指针和数组
六:二级指针
七:指针数组
🥑🍓Let's go🍓🥑
一:指针是什么?
指针理解的2个点:
1.指针是内存中一个最小单元的编号,也就是地址。
2.平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
【总结】:指针就是地址,口语中说的指针通常指的是指针变量。
指针变量:可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量。
🐶🐶具体举例:
#include <stdio.h>
int main()
{
int a = 10;//内存中开辟一块空间
int* pa = &a;
//变量a,取出它的地址,可以使用&操作符
//a变量占用4个字节的空间,
//这里是将a的4个字节的第一个字节的地址存放在p变量中,
//p就是一个指针变量。
return 0;
}
✍️✍️【注】:指针的大小在32位平台是4个字节,在64位平台是8个字节
二:指针和指针类型
2.1 指针
🐱 🐱具体举例:
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
*pa = 20;
printf("a=%d", a);
return 0;
}
2.2 指针类型
🐱 🐱具体举例:
#include <stdio.h>
int main()
{
int * pa = NULL;
char * pb = NULL;
short * pc = NULL;
long * pd = NULL;
float* pe = NULL;
return 0;
}
等等
2.3 指针+-整数
🐱 🐱具体举例:
#include <stdio.h>
int main()
{
int n = 10;
char* pa = &n;
int* pi = &n;
printf("%p\n", &n);
printf("%p\n", pa);
printf("%p\n", pa+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}
🉐🉐:
㊙️㊙️ 分析如下:
✍️✍️【总结】:指针的类型决定了指针向前或者向后走一步有多大(距离)
2.4 指针的解引用
🐱 🐱具体举例:
#include <stdio.h>
int main()
{
int n = 0x11223344;
char* pa = &n;
int* pi = &n;
*pa = 0;
*pi = 0;
return 0;
}
🉐🉐:
n的地址与在内存中存储的内容
*pa=0后得到下图。分析如下:因为pa的类型是char类型,为一个字节。
所以只能改变一个字节的内容。
*pi后得到下图。分析如下:因为pi的类型是int类型,为四个字节。
所以可以改变4个字节的内容。
【总结】:
char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
三:野指针
3.1野指针成因
3.1-1 指针未初始化
🦊🦊具体举例:
#include <stdio.h>
int main()
{
int* p;//局部变量指针未初始化,默认为随机值
*p = 20;//非法访问内存
return 0;
}
3.1-2 指针越界访问
🦊🦊具体举例:
#include <stdio.h>
int main()
{
int arr[10] = {0};
int* pa = arr;
int i = 0;
for (i = 0; i <= 10; i++)
{
*pa = i;
pa++;
}
return 0;
}
☀️☀️分析得:
3.1-3 指针指向的空间释放
🦊🦊具体举例:
#include <stdio.h>
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
*p = 20;
return 0;
}
☀️☀️分析得:
3.2如何规避野指针
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放,及时置 NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
🦝🦝具体举例:
#include <stdio.h>
int main()
{
当前不知道p应该初始化为什么地址的时候,直接初始化为NULL
int* p = NULL;
明确知道初始化的值
int a = 10;
int* p = &a;
注意:
当定义指针变量初始化为NULL后,不可在对其进行赋值
int* pa = NULL;
*pa = 10;
所以在初始化指针时,先判断是否为NULL,在进行初始化
int* pb = 10;
if (pb != NULL)
{
*pb = 20;
}
return 0;
}
[注]:1.空指针可以确保不指向任何对象或函数;而未初始化指针则可能指向任何地方。
2.空指针与任何对象或函数的指针值都不相等
3.任何指针都可以转化未void*
4.void*可以转化为任何指针
5.指针虽然高效、灵活但是可能不安全
四:指针运算
4.1指针+-整数
🐼🐼具体举例:
#define N 5
#include <stdio.h>
int main()
{
float arr[N];
float* vp;
//指针+-整数:指针的关系运算
for (vp = &arr[0]; vp < &arr[N];)
{
*vp++ = 0;
}
return 0;
}
☀️☀️分析得:
4.2指针-指针
🐼🐼具体举例:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
char ch[5];
int ret = &arr[9] - &arr[0];
printf("%d\n",&arr[9]-ch[3]);//这里是错误的
printf("ret=%d\n", ret);
return 0;
}
☀️☀️分析得:
【注】:指针和指针相减的前提是:两个指针指向的是同一块空间。
4.3指针的关系运算
🐼🐼具体举例:
#define N 5
#include <stdio.h>
int main()
{
int arr[N];
int* vp;
for (vp = &arr[5]; vp > &arr[0];)
{
*--vp = 0;
}
return 0;
}
☀️☀️分析得:
代码简化得:
#define N 5
#include <stdio.h>
int main()
{
int arr[5];
int* vp;
for (vp = &arr[N - 1]; vp > &arr[0]; vp--)
{
*vp = 0;
}
return 0;
}
☀️☀️分析得:
【注】:标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,
但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
五:指针和数组
🐯 🐯 具体举例:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);//数组名是数组首元素的地址
printf("%p\n", &arr[0]);
return 0;
}
🉐🉐
【注】:数组名是数组首元素的地址
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%p<==>%p\n", &arr[i], p + i);
}
return 0;
}
🉐🉐
六:二级指针
🐨🐨具体举例:
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;//pa是指针变量,一级指针
int** ppa = &pa;//ppa是一个二级指针
//*ppa表示是一个指针变量,int* 表示ppa的类型
* pa == a;
**ppa == a;
return 0;
}
七:指针数组
🐯 🐯具体举例:
#include <stdio.h>
int main()
{
int arr[10];//整形数组 - 存放整形的数组就是整形数组
char ch[5];//字符数组 - 存放的是字符
//指针数组 - 存放指针的数组
int* parr[5];//整形指针的数组
char* pch[5];
return 0;
}