目录
1. 指针是什么
在了解指针之前我们需要知道什么是内存
内存是储存区域,内存中每个空间都有独立的一个编号(类似于每个人的学号)
内存编号就是地址也就是指针
指针理解的2个要点:
1. 指针是内存中一个最小单元的编号(1个字节),也就是地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
指针变量:
我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量。
printf("%p",p);
return 0;
}
00EFFB1C就是a的地址,存放在指针变量p中
指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
注:
指针变量是用来存放地址的,地址是唯一标示一个内存单元的。
指针的大小在32位平台是4个字节,在64位平int num = 10; p = #
台是8个字节(所有指针类型)
2. 指针和指针类型
我们知道在变量中有不同的类型,整形,浮点型等
而在指针中也有这样的类型
当有这样的代码:
int num = 10;
p = #
要将&num(num的地址)保存到p中,我们知道p就是一个指针变量,那它的类型是怎样的呢?
我们给指针变量相应的类型
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
其实:
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。
如果用char*类型的指针存放int类型的变量会发生什么?
#include<stdio.h>
int main()
{
int a = 0x11223344;
int*pa = &a;
char* pc = &a;
*pc = 0;
return 0;
}
a的地址存放的数据是0x11223344(由于编译器是小端存储所以内存中存储顺序是44 33 22 11)
而我们把int a的地址存放在一个char*类型的指针变量中,再对指针变量进行解引用修改a的值时,可以观察到只修改了1个字节的地址
总结: 不同的指针类型决定了指针进行解引用操作时,指针的权限有多大(例如:int*类型的指针只能访问4个字节的空间,char*类型的指针只能访问1个字节的空间)
2.1 指针+-整数
看下面这段代码:
#include<stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = &a;
printf("pa=%p\n", pa);
printf("pc=%p\n", pc);
printf("pa+1=%p\n", pa+1);
printf("pc+1=%p\n", pc+1);
return 0;
}
通过运行程序我们可以发现:
int*类型的指针+1,地址增加4(int*+1,跳过一个整形,向后走4个字节)
char*类型的指针+1,地址增加1(char*+1,跳过一个字符,向后走1个字节)
总结:指针的类型决定了指针的步长,向前或者向后走一步有多大(距离)
2.2 指针的解引用
通过指针变量存储的变量地址访问变量,可以改变变量大小
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;
*p = 5;
return 0;
}
2.3 指针类型的用途
总结:
1.不同的指针类型决定了指针进行解引用操作时,指针的权限有多大
2.指针的类型决定了指针的步长
3. 野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
3.1.1 指针未初始化
#include <stdio.h>
int main()
{
int* p;//局部变量指针未初始化,默认为随机值,这时候p就为野指针
*p = 20;
return 0;
}
3.1.2 指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
当指针访问其他未申请空间时,就会造成指针越界访问
3.1.3 指针指向的空间释放
#include<stdio.h>
int* test()
{
int num = 100;
return #
}
int main()
{
int* p = test();//p接收了1个已经释放的局部变量的内存地址
*p = 200;//对已经释放的地址进行修改,会造成野指针非法访问
return 0;
}
3.2 如何避免野指针的形成
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放,及时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;//明确初始化
//NULL - 0,就是初始化指针的
int* p = NULL;
return 0;
}
4. 指针运算
4.1 指针+-整数
#include<stdio.h>
int main()
{
int arr[5] = {1,2,3,4,5};
int *p = arr;
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
通过指针+-整数进行指针的偏移,访问其他地址
4.2 指针-指针
注:指针+指针无意义
指针-指针计算指针之间的元素个数:
#include<stdio.h>
int main()
{
//两个指针相减的前提是:指针指向的同一块连续的空间
int arr[10] = {0};
printf("%d\n", &arr[9] - &arr[0]);
printf("%d\n", &arr[0] - &arr[9]);
return 0;
}
注:
两个指针相减的前提是:指针指向的同一块连续的空间
4.3 指针的关系运算
比较指针之间的地址大小
#include<stdio.h>
int main()
{
for (vp = &values[5]; vp > &values[0];)
{
*--vp = 0;
}
return 0;
}
5. 指针和数组
我们先看这段代码:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
运行结果:
可见数组名和数组首元素的地址是一样的
结论:数组名表示的是数组首元素的地址(2种情况除外)
例外:1. sizeof(arr)2. &arr
6. 二级指针
指针变量也是变量,是变量就有地址,指针变量的地址存放在二级指针中
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;//p就是指针变量,一级指针变量
int** pp = &p;//pp就是二级指针
return 0;
}
p存放a的地址,pp存放p的地址
对于二级指针的运算有:
p通过对p中的地址进行解引用,这样找到的是 a , *p其实访问的就是 a
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;
*p = 20;
return 0;
}
**pp 先通过 *pp找到 p ,然后对 p 进行解引用操作: *p ,那找到的是 a
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;//p就是指针变量,一级指针变量
int** pp = &p;//pp就是二级指针
**pp = 50;
return 0;
}
7. 指针数组
指针数组是指针还是数组?
答案:是数组。是存放指针的数组
数组我们已经知道整形数组(存放整形的数组),字符数组(存放字符的数组)。
int arr1[5];
char arr2[6];
指针数组:
int* arr3[5];//存放整型指针的数组
char* arr4[6];//存放字符指针的数组
用途:
1.直接存放整形地址
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int e = 50;
int* arr3[5] = {&a, &b, &c, &d, &e};//存放整型指针的数组
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *(arr3[i]));
}
return 0;
}
2.用一维数组模拟一个二维数组
#include<stdio.h>
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");
}
return 0;
}
结果: