目录
前言
本篇是对指针知识的初级理解,后续还会有文章带大家了解指针
一、指针是什么?
指针是什么?
1、每个内存单元都有一个编号,把内存单元的编号就称为地址(地址也叫指针)
一个内存单元是一个字节
2、指针是内存中一个最小单元的编号,也就是地址
3.平时口中说的指针,通常指的是指针变量,是用来存放内存地址的变量
4.指针的大小在32位平台是4个字节,在64位平台是8个字节。
原因:在32位的机器上,地址是32个bite组成二进制序列,因为32个bit就是4字节那地址就得用4个字节(1byte = 8 bit)的空间来存储,
所以 一个指针变量的大小就应该是4个字节。
那如果在64位机器上,有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。 )
指针就是地址
指针变量
我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量,简而言之就是一个变量,变量里面放的是地址
写c语言程序的时候,创建的变量,数组等都要在内存上开辟空间
a变量占4个字节,这里把a的4个字节的第一个字节的地址放在p变量中。
指针变量在32位的平台是4字节,在64平台是8字节
pa的类型是int *,*意味着pa是指针,int意味着pa指向的那个变量是int类型
总结;指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
二、指针和指针类型
1、指针的大小:
在32位平台是4个字节,在64位平台是8个字节。
当不知道给这个指针赋值什么时,就赋值为NULL 。(避免野指针后面会讲)
sizeof 返回值的类型是无符号整型 unsigned int,严格意义上打印时应该用%u,不能用%d,%zu打印最准确,%zu本身就是给sizeof准备的一种格式打印。
2、指针类型的意义
整型有类型 int char short…,指针变量也有类型
讲解:
意义1:指针的类型决定了指针在被解引用的时候可以访问几个字节(指针的权限)
Int*的指针解引用访问4个字节
Char*的指针解引用访问1字节
意义2:指针类型决定指针加一减一操作时的步长(跳过多少字节)
指针的类型决定了指针±1操作的时候,跳过几个字节,即指针的步长。
如果是int的指针,+1跳过4个字节
如果是char的指针,+1跳过1个字节
当然,指针不仅可以±1,指针±整数时也很类似。
前后是统一的,解引用访问多少字节,加一减一跳过几个字节。
type* p;
跳过的是:n*sizeof(type)这么多个字节。
三、野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的),比如一个全部变量不初始化。
1、野指针成因
指针未初始化
局部变量指针没有初始化,默认是随机值
指针越界访问
数组只有10个元素但是通过指针访问了不属于这个数组的空间所以是越界访问
指针所指向的空间释放了
a是一个局部变量,进入函数就创建,离开函数则还给操作系统
2、如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放及时置NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
int main()
{
int a = 10;
int* p = &a;
int* ptr = NULL;//ptr是一个空指针,没有指向任何有效的空间。这个指针不能直接使用
//int* ptr2;//野指针
if (ptr != NULL)
{
//使用
}
return 0;
}
明确知道指针应该初始化为谁的地址,就直接初始化
不知道指针初始化为什么值,暂时初始化为NULL
四、指针运算
1、指针±整数
将数组中的元素都赋值为1
不使用下标访问操作符
int main()
{
int arr[10] = { 0 };
//不使用下标访问数组
int* p = &arr[0];
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
*p = 1;
p++;//p = p+1
}
p = arr;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));//p+i
}
/*for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}*/
return 0;
}
讲解:使用指针来实现给数组里面的元素赋值。因为p是指针,可以通过指针加上数字找到下一个元素的地址之后解引用来赋值。
//int arr[10]
//int* p = arr;
//*(p+i) == arr[i]
*(p+i)和arr[i]是一样的。arr是数组名是首元素的地址
来看这些等价的表达:
arr[i]==*(arr+1)==*(i+arr)==i[arr];
可能对最后一个会不太理解。
2+3->3+2;[]是操作符而已和加号一样
那arr[i]-->i[arr],arr和i只是两个操作数而已,和3和2一样
指针之间是可以比较大小的;
2、指针-指针
指针 - 指针的绝对值得到的是指针和指针之间元素的个数
但是指针相减有前提:要指向同一块空间
例子:自定义函数求字符串的长度
方法一:
方法二:递归:
方法三:指针-指针(得到指针和指针之间元素的个数,可以得到字符串的长度)
指针-指针得到的数值的绝对值:是指针和指针之间的元素个数
指针-指针运算的前提条件是:指针和指针指向了同一块空间。
先保存首元素的地址,之后返回末尾和首元素地址的差
因为s在找到字符串结尾的'\0'时还会做一次加1操作,所以在计算长度时应该减去这一次额外的加1操作
这个就多加了一次,s++还是a的地址因为是后置加加,解引用用完之后s到下一个值的地址
*s++这个操作在C语言中会先执行 s++这个操作,然后才会执行解引用操作。. 这是因为在C语言的操作符优先级中,后缀自增++的优先级(级别2)要高于解引用*(级别3)。所以,先对 :执行后缀自增操作,使得:指向下一个元素,然后再进行解引用操作取出 s指针指向的内容。
因此,*s++意味着先取出当前 :所指向的元素的值,然后再将 :让其指向下一个元素
3、指针的关系运算(比较大小)
最后两个指针相等了
五、 数组和指针
指针变量就是指针变量,大小是4或者8,用来存放地址的
数组就是数组,数组一块连续的空间,可以存放1个或多个类型相同的数据
联系:数组名是数组首元素的地址也就是指针
当我们知道数组首元素的地址的时候,因为数组是连续存放的,所以通过指针就可以遍历访问数组
数组是可以通过指针来访问
int arr1[10]
int ar2[8];这两个数组的类型一样吗?
讲解:不一样去掉数组名就是类型,一个是int[10],一个是int[8]
六、二级指针
创建一个变量p,他是一级指针,变量实在内存中开辟空间的,它自己也有自己的地址
这个*说明p是指针,前面的int说明p指向的那个变量的类型是int
同理*说明pp是指针,int*说明pp指向的变量的类型是int*
七、指针数组
指针数组是指针还是数组?
是数组。是存放指针的数组。
整型数组 存放整型的数组
字符数组 存放字符的数组
指针数组 存放指针(地址)的数组
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
//指针数组
int* parr[] = { arr1, arr2, arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
//printf("%d ", *(parr[i] + j));
//printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
这个parr就是指针数组,它的每个元素都是char*类型。长这样char* parr[]
这个parr就是指针数组,它的每个元素都是char*类型。长这样char* parr[]
Parr[1]这个数组的第一个元素是arr1,arr1又是数组名是arr1数组的起始地址(第一个元素的地址)
为什么是打印整个数组呢,我们看个简单的例子
怎么样是不是就明白了
用一个指针数组模拟出一个二维数组
等价的写法
后面还会学习指针数组的~