目录
1.指针的引入
指针就是地址
(1)指针的大小:在32位平台是4个字节,64位平台是8个字节。所有类型指针的大小都是相同的,为4字节或8字节,因为指针是用来存放地址的,指针的大小取决于地址的存储需要多大空间(即地址线的多少,32位平台,32根地址线或64位平台,64根地址线)。
(2)指针变量:用来存放地址,表示形式:变量类型 *变量名(如int *pa)。
等价: *pa<=>a ;pa<=>&a。取地址,取得是第一个字节的地址,如int类型占4个字节,用“&”取int a的地址,&a=第一个字节的地址。
#include<stdio.h>
int main()
{
int a = 10;//a在内存中要分配的空间 - 4个字节
printf("%p\n", &a);//%p专门用于打印地址的
int* pa = &a;//pa是用来存放地址的,在c语言中叫做指针变量
//*说明pa是指针变量
//int说明pa指向的对象是int类型的
*pa = 20;//* 解引用操作,*pa就是通过pa里边的地址找到a,更改a的值
printf("%d\n", a);//输出20
return 0;
}
2.初级指针
指针类型的意义:
(1)指针类型决定了解引用的权限有多大,整型指针解引用能访问4个字节,字符指针解引用能访问1个字节,double类型解引用能访问8个字节。
(2)指针类型决定了指针走一步能走多远(步长)。整型指针+1,步长为4;字符指针+1步长为1;double类型+1步长为8。
图解:
3.野指针
(1)概念:野指针是指指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
(2)形成原因:
①指针未初始化
int main()
{
//这里的p就是一个野指针
int* p;//p是一个局部的指针变量,局部变量不初始化的话,默认是随机值
*p = 20;//非法访问内存了
return 0;
}
②指针越界访问
int main()
{
//指针越界访问
int arr[10] = { 0 };
int* p = arr;
for (int i = 0; i <= 10; i++)
{//当指针指向的范围超出数组arr的范围时,p就是野指针
*p = i;
p++;
}
return 0;
}
③指针指向的空间释放
int* test()//返回a的地址,需用一个指针变量来存储
{
int a = 10;
return &a;//a是局部变量,进入test函数创建,离开即释放
}
int main()
{
int* p = test();
*p = 20;
return 0;
}
(3)如何规避野指针
指针初始化(当不知道指针p应该初始化为什么地址的时候,直接初始化为NULL)、小心指针越界、指针指向空间释放,应及时将其置为NULL、指针使用前进行有效性判断。
4.指针运算
(1)指针±整数=某个元素的地址
例:利用指针打印数组内容
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\n", *p);
p++;
}
return 0;
}
(2)指针- 指针=两指针之间的元素个数(前提:两指针指向同一块空间 )。
例1:利用指针求元素个数
int main()
{//两指针之间的元素
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", &arr[9] - &arr[0]);//输出9
return 0;
}
例2:利用指针求字符串长度
int my_strlen(char* str)//用字符指针char* str来接收a的地址
{//函数计算完字符串长度,返回整型
char* start = str;//记录起始地址
while (*str != '\0')//计算结尾地址
{
str++;
}
return str - start;//返回元素个数
}
int main()
{
char arr[] = "abc";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
(3)指针的关系运算
例:更改数组元素的值,将其置为0
①法一
for循环,首先将数组最后一个元素a[4]后面的地址赋给pa指针,判断满足条件,指针指向前一个元素即a[4],再“*”解引用将a[4]的内容置为0。判断满足条件,将指针指向前一个元素,即a[3],再解引用将a[3]内容置为0,以此类推。
②法2
for循环,首先将数组最后一个元素a[4]的地址赋给pa指针,判断满足条件,“*”解引用更改a[4]的内容为0,再将指针指向前一个元素。判断满足条件,解引用更改a[3]的内容为0,指针指向前一个元素a[2],以此类推。
总结: 第二种方法虽然可行,但应尽量避免。因为标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但不允许与指向第一个元素之前的那个内存位置的指针进行比较。