1.指针是什么
2.指针和指针类型
3.野指针
4.指针运算
5.指针和数组
6.二级指针
7.指针数组
1.指针是什么
在计算机中,指针是编程语言中的一个对象,利用地址,他的值直接指向存在电脑储存器中的另外一个地方的值。由于通过地址找到所需的变量单元,可以说,地址指向该变量单元,因此,将地址形象化的称为‘指针’。意思是能通过他找到以他为地址的内存单元
指针就是个变量,存放内存单元的地址(编号)
一个指针单元的内存大小是一个字节
int main()
{
int a = 10;
int* p = &a;//int* p就是一个指针变量,用来存放地址,也就是a的地址,也可以说&a也是个指针
}
总结:指针就是变量,用来存放地址(存放在指针中的值都被当成地址处理)
在32位的机器上,地址是32个0或1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址
2.指针和指针类型
int main()
{
printf("%d\n, sizeof(*int));
printf("%d\n, sizeof(*char));
printf("%d\n, sizeof(*short));
printf("%d\n, sizeof(*double));
return 0;
}//从这里得出,指针的大小都是四个字节
指针类型都是四个字节
那指针类型有何意义?
指针类型的意义,指针类型决定了指针进行解引用操作的时候,能够访问空间的大小
当是int* p的时候,*p能够访问4个字节的大小
是char8* p的时候 ,*p只能够访问1个字节的大小
doule* p 的时候,*p能够访问8个字节的大小
指针+-整数
#define _
#include<stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);
printf("%p\n", pa+1);
printf("%p\n", pc);
printf("%p\n", pc+1);
return 0;
}
通过上面的打印我们发现,程序出来分别答案是
0095FB58(int*类型的指针本来是8)
0095FB5C(但是在+1之后变成了c,说明改变了)
0095FB58(char*类型本来是8)
0095FB59(但是在+1之后却才变9)
所以指针类型又决定了:指针走一步走多远
当是int* p的时候:p+1是指针向后平移了四个字节
当是char* p的时候,p+1是指针向后平移了1个字节
当是double* p的时候p+1是指针向后平移了8个字节
总结:指针的类型决定了指针向前或者向后移动的步子距离,指针的类型决定了,对指针解引用的时候能有多大的权限(能操作几个字节)
3.野指针
野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
野指针成因
1.1指针未初始化
int main()
{
int a;//局部变量不初始化,默认值是随机值
int *p;//局部的指针变量,不初始化,就被初始化随机值
*p = 20;//
// 如果我要解引用p,赋值给他20进去,但是由于他没有初始化,所以他没有指定指向的对象空间,是随机的放进去
return 0;
}
1.2指针的越界访问
#define _
#include<stdio.h>
int main()
{
int arr[10] = {0};
int *P = arr;
int i = 0;
for(i=0; i<=11; i++);
{
//当指针指向的范围超出了数组的范围是,p就是野指针
*(p++) = i;
//*p = i;与上面的方法等价
// *p++;
} //数组的范围只能存10个元素,而这里却+到了12个元素,所以就是野指针 ,非法越界访问
return 0;
}
1.3指针指向的空间释放
int *test()
{
int a = 10;//这是一个局部变量,当创建了一个a=10的时候他就自动销毁了
return &a;//这时候我取出a的地址返回去给int *p,他接收了a的地址
}
int main()
{
int *p = test()//接收了a的地址,这时候已经存了a的地址
*p = 20;//这时候解引用p通过p找到a的地址,然后把它的内容改成20,它指向了一块已经被空间释放的内存块,所以就是野指针
return 0;//但是,像这样的操作就称为野指针,因为a =10已经销毁了,空间释放了,
}
如何避免野指针
1.指针初始化
2.小心指针越界
3.指针指向空间释放及时设置NULL(空指针),如果不知道初始化什么,就赋值空指针,和(int a = 0);一个道理
4.指针使用之前检查有效性
4.指针运算
指针+-整数
指针-指针
指针的关系运算
4.1指针运算
指针+一个整数,用指针的方法不用下标的方法访问元素
#define _
#include<stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i =0;
int sz = sizeof(arr)/sizeof(arr[0]);//计算元素个数
int* p = arr;//放着数组第一个元素的地址
for(i=0, i<=sz, i++)
{
printf("%d ", *p);//*p放着第一个元素的地址,打印第一个
p = p + 1;//p+1向后跳一个整型,这时候就指向了2,下次循环的时候就是2+1了,以此类推
//p++//也可以换成p++,一样的效果
}//减法也是同样的道理
}
4.2指针-指针
#include<stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d ", &arr[9] - &arr[0]);//指针减去指针得到的是中间元素的个数,0和9之间的个数为9所以程序执行的结果也是9
return 0;//如果想得到元素个数必须是大数减去小的数,不然像上面的我把9改成0,0改成9,那得到的答案是-9是个负数
}
指针-指针是得到中间元素的个数,大值减去小值得到的是他的元素个数,如果是小值减去大值,它的绝对值才是元素的个数,指针与指针相减,必须是同一类型的都是指向同一块空间的才能相减,不是指向同一块空间的不能向减,比如一个int类型的去减char类型的,这是错误的
4.3指针的关系运算
说白了就是比较大小
5指针和数组
数组是可以通过指针来进行访问的
通常说数组名是首元素地址,但是有下面这两个例外
5.1.&数组名,数组名不是首元素地址,数组名代表整个数组的地址,取出的是整个数组的地址
5.2.sizeof(数组名),数组名是代表计算整个数组的大小,单位是字节
6.二级指针
int main()
{
int a = 10;
int * pa = &a;//int* 就是一级指针类型,pa就是一级指针,把a的地址放到pa里去
int* * ppa = &pa;//int** 是二级指针类型 ppa就是二级指针,把pa的地址放到ppa里去
return 0;
}
把二级指针ppa类型(int * *) 分解来看,最右边这个'*',表达ppa是一个指针, 而(int *)表达ppa指向的那个对象'pa',是(int *)类型
如果想通过ppa的地址找出a的值并打印出来,可以这样写
int main()
{
int a = 10;
int * pa = &a;
int* * ppa = &pa;
printf("%d\n", **ppa);//*ppa可以找到pa的地址,然后pa里面存放着a的地址,
//a就存放着a的值,所以我们可以通过两次解引用ppa来找到a的值,ppa->pa->a->10
//**ppa = 20;// 如果想把a的值改掉,也可以通过这样的方法解引用两次
return 0;
}
7.指针数组
指针数组---(本质这是个数组)
数组指针---(本质这是个指针)
7.1指针数组
存放指针的数组
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[3] = {&a,&b,&C};//指针数组
// arr[0]//这就是第一个元素地址arr[1],这是第二个元素的地址arr[2],这是第三个元素的地址
}