理解指针的概念
今天要来复习的呢就是大一基本没有学过的东西,我们大一的老师可能是刚刚从国外回来,一上课就说指针没有用,了解了解就ok(想起我们的数据结构老师也是国外来的,我的意思是...),我个人觉得吧,这个东西我不知道它以后在工作或者日后的学习来说重不重要,但是就目前来说,我反正觉得这玩意算得上是重点难点!
首先我们还是来了解一些指针是什么东西,指针最本质来理解就是地址,我觉得搬书上概念来说也讲不清楚指针到底是什么,我们通过解释一些东西来理解。
指针的类型:
(1)int*ptr;//指针的类型是int*
(2)char*ptr;//指针的类型是char*指针所指向的类型
(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char从上面两个东西来看是不是就可以把指针理解成为一种变量,一种地址变量!
再来接着看下面的东西:
int *p;
int a = 1;
p = &a;
//&a 的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a 的地址。
这样一句话就把这个指针的类型,和它指向那个东西的类型,和它指向的是什么东西
都清楚的描述出来了(通俗一些:把a的地址放到p这个指针变量里面,p这个指针变量的类型就是int*,所指向的a的地址的类型是int,指向a的地址什么意思?就是去访问a的地址,访问a的地址什么意思?那不就是把a所储存的东西搞出来吗)
再来看下面这段代码
#include<stdio.h>
int main()
{
int* p;
int a = 1;
p = &a;
printf("%d\n",a);
printf("%p\n",&a);
printf("%d\n",*p);
printf("%p",p);
return 0;
}
/*
1
0x7ffe1fd7bbfc
1
0x7ffe1fd7bbfc
*/
是不是,感觉就有点好理解了,p是一个指针变量,是用来储存地址的,现在把a的地址放进去,p呢是一个指针变量,这个指针类型是int*,指向的类型是int,指向的东西是a的地址也就是&a,所以输出p就是输出a的地址,那么为什么*p的意思是指向a的地址呢?那么接下来我们来谈谈*和&这个两个东西
*和&
&是取地址运算符,*是间接运算符(指向)。
我们看上面的代码,出现过两次*p,一次是在定义指针变量p的时候出现了,一次是打印*p,有什么区别呢?前面阿只是说明p这个变量是指针变量,是int*,而后面这个则代表指向指针变量p的右值,而指针变量p的右值是a的地址,也就是当我们输出*p的时候就是指向了a的地址,指向a的地址就是访问a,那不就是输出了a的值了。所以指针变量p的右值仅仅是保存了a的地址,不是直接就可以输出a的值,而是要通过*这个间接运算符去访问地址!可以看出
*p ==> * &a ==> a
*&是可以消掉的
到这里我觉得基本的指针定义就很清楚了。
野指针与空指针
-
野指针
野指针是一个指向未知的,不确定地方的指针.
"未知的","不确定的" ,指向的地方可能存在,也可能不存在.
可能可以访问,也可能不可以访问.
对野指针的访问,会有什么后果?
可能可以访问的,可能不可以访问(导致非法的内存访问).
非法的内存访问 :
不存在的地方,你去访问
存在但不能写,你去写
存在但不能读,你去读
非法的内存访问 ,会导致"segmentation fault 段错误",系统把你的进程给 kill 掉.
例子 :
-
int *p;
//定义了一个指针变量p,你没有赋予初始值,不代表p没有值,相反p一定会有一个值
//意思是p一定会指向一个地方,但是这个值是多少,指向哪里,你是不知道(未定的,未知的)
请问 p是不是一个野指针 ? A
A 是 B 不是
-
int *p;
int a;
.....
p = &a;
请问 p是不是一个野指针 ?B
A 是 B 不是
-
#include<stdio.h>
int main()
{
int* p;
int a ;
p = &a;
printf("%d\n",a);
printf("%p\n",&a);
printf("%d\n",*p);
printf("%p",p);
return 0;
}
//我们知道整型不给初始值它会默认给个0的,所以阿
//这样不会产生野指针
-
空指针
空指针是一个指向空(不存在的地方,NULL)的指针.
空指针不是野指针,因为指向一个确定的地方(尽管这地方不存在).
对空指针的访问,一定会导致 "非法的内存访问"(段错误)
int *p = NULL;//p空指针 *p //用*p就会出现段错误
指针的加减
前面说到,指针本质上是一个地址,那么地址的加减是什么鬼呢?他这个加减呢就不是单纯的加一减一了,而是加上一个所指向类型的字节。减法同理,在做加减法运算的时候并不会改变其类型。
#include<stdio.h>
int main()
{
double a;
int *p;
p = (int *)&a;
printf("p = %p\n",p);
printf("p + 1 = %p\n",p + 1);
return 0;
}
/*
p = 0x7fff2df088c8
p + 1 = 0x7fff2df088cc
*/
指针和数组
很明显,这玩意可以与数组结合起来,因为我们前面说过了数组在开辟空间的时候是连续的开辟的,那么指针可以通过加减法来访问地址,那不就可以通过指针的加减法来遍历数组
#include<stdio.h>
int main()
{
int a [4] = {1,2,3,4};
printf("%d\n",*(&a[0]+1));
for(int i = 0; i < 4;i++)
{
printf("%d\n",*(&a[0] + i));
}
return 0;
}
看到第二个printf那里,是数组a的地址做加减法然后再使用了*这个间接运算符去指向,所以就可以遍历数组了。
我们在定义一个数组的时候 int a[],a就是这个数组的数组名,这个数组名在有的时候是代表指针,有的时候是代表整个数组就以这个数组为例来看下面的总结:
(1) 在如下情况,a代表整个数组
sizeof(a) 求数组 a 所占的字节数,此时 a 代表整个数组
typeof(a) 求 a 的类型 ,此时 a 代表整个数组
&a a的地址,此时a代表整个数组,整个数组的地址(可以通过数组的首地址来访问这个数组)
(2) 其他情况下,当指针用
p = a;//a当指针用,why? 不能把整个数组赋值给其他对象.
//a 当作指针
p = a;==> p = &a[0];
a + 1 ;//a当做指针用,why?整个数组不能整体 + 1
a + 1 ==> &a[0] + 1 ==> &a[1]反正除了(1)中提到的三种情况那就都是当指针用的
#include <stdio.h>
int main()
{
int a[10] = {0,1,2,3,4,5,6,7,8,9};
printf("%ld\n",sizeof(a));//sizeof(a) a此时代表整个数组,
//sizeof(a) 求整个数组所占的字节数
printf("%p\n",a);//此时a当做指针来用,a ==> &a[0]
printf("%p\n",&a[0]);
printf("%d\n",*a);//不是三种情况之一,所以当指针用,指向第一个数
printf("%d\n",*(a + 1));
//*(&a[0] + 1)==>*(&a[1]) ==> a[1]
return 0;
}