1.指针的定义
1. 指针是内存中一个最小单元的编号,也就是地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
故指针就是地址,口语中说的指针是指针变量
指针变量
是通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量
(a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量)
指针变量,用来存放地址的变量(存放在指针中的值都被当成地址处理)
- 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节
- 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址
总结:
-
指针是用来存放地址的,地址是唯一标示一块地址空间的
- 指针的大小在32位平台是4个字节,在64位平台是8个字节(一个字节是八个比特位)
2.指针和指针类型
指针类型:
type+*
#include<stdio.h>
int main()
{
char *pc = NULL;//char* 类型的指针是为了存放 char 类型变量的地址。
int *pi = NULL;//short* 类型的指针是为了存放 short 类型变量的地址。
short *ps = NULL;//int* 类型的指针是为了存放 int 类型变量的地址。
long *pl = NULL;//...
float *pf = NULL;//....
double *pd = NULL;//...
return 0;
}
指针的解引用
int main()
{
int arr[10] = { 0 };
int* p = arr;
char* pc = arr;
printf("%p\n", p);
printf("%p\n", p + 1);
printf("%p\n", pc);
printf("%p\n", pc+ 1);
int a = 0x11223344;
char* pb = &a;
*pb = 0; //重点在调试的过程中观察内存的变化
int* pa = &a;
*pa = 0;
return 0;
}
指针类型意义:
- 决定了指针向前或者向后走一步有多大(距离)
- 决定了,对指针解引用的时候有多大的权限(能操作几个字节)
- 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
3.野指针
定义:
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
原因:
- 指针未初始化
- 指针越界访问
- 指针指向的空间释放
int main()
{
//这里的p就是一个野指针
int* p;//p是一个局部的指针变量,局部变量不初始化的话,默认是随机值
*p = 20;//非法访问内存
//2.越界访问
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++)
{
*p = i;
p++;
}
return 0;
}
//3.指针指向的空间释放
int* test()
{
int a = 10;
return &a;//这里在函数取了a的地址
}
int main()
{
int* p = test();//但是在函数结束后,a的地址也被释放了所以p指向的是空地址
*p = 20;
return 0;
}
如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放及时放置NULL(空指针)
- 避免返回局部变量的地址
- 指针使用之前检查有效性
4.指针运算
指针+-整数
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int* pend = arr + 9;
while (p <= pend)
{
printf("%d ", *p);
p++;
}
return 0;
}
指针-指针
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", &arr[9] - &arr[0]);
return 0;
}
my_strlen(char* str)//模拟strlen函数
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
int main()
{
int len = my_strlen("abc");
printf("%d\n", len);
return 0;
}
指针关系运算
#define N_VALUES 5
float values[N_VALUES];
float *vp;
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;//运算关系,先减减在使用
}
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较。
5.指针和数组
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);//结果与上一行一致
return 0;
}
数组名是首元素的地址
数组与指针的联系
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);// p+i 其实是数组 arr 下标为i的地址
*(p + i) = i+1;
}
printf("%d\n", 2[arr]);
printf("%d\n", arr[2]);
printf("%d\n", p[2]);//p[2]-->*(p+2)
//arr[2]-->*(arr+2)——>*(2+arr)——>2[arr]
//[]是一个操作符 2和arr是两个操作数
//arr[2] <==> *(p+2) <==> *(2+p) <==> *(2+arr)<==>*(arr+2)
//2[arr]<==>*(2+arr)
return 0;
}
6.二级指针
int main()
{
int a = 10;
int* pa = &a;//pa是指针变量,一级指针
//ppa就是一个二级指针变量
int* *ppa=&pa;//pa也是个变量,&pa取出pa在内存中起始地址
printf("%p\n", pa);
printf("%p\n", *ppa);
//以此类推也有***pppa......
//*ppa==pa *pa==a **ppa==a pa==*ppa
return 0;
}
7.指针数组
int main()
{
int arr[10];//整形数组 - 存放整形
char ch[5];//字符数组 - 存放字符
//指针数组 - 存放指针的数组
int* parr[5];//整形指针的数组
char* pch[5];//字符指针的数组
return 0;
}