C语言:关于指针学习以及理解
文章目录
- C语言:关于指针学习以及理解
- 一、什么是指针
- 二、为什么使用指针、什么情况下使用指针
- 三、如何使用指针
- 四、使用指针要注意的问题
- 五、指针与数组的关系
- 六、指针的运算
- 七、指针与const配合
- 八、什么是二级指针,什么情况下使用
- 九、指针函数和函数指针
- 十、数组指针和指针数组
- 十一、结构体指针
- 十二、结构体成员指针
- 十三、指针与堆内存配合
一、什么是指针
首先,指针是一种数据类型,使用它定义的变量叫指针变量。指针变量是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址,指针的值是一个地址。
指针的四个要素?:指针的类型、指针所指向的类型、指针的值 (指针所指向的内存区,即指向的地址) 、指针本身所占据的内存区(指针本身的地址)。
(根据上图)举个例子:
int a=111;
int *p = &a;
上面两句语句中可得出:
p 是指针变量
指针类型: p 是一个 int 类型的指针
指向类型: p 指向一个 int 类型的数据 a
指针的值: p 的值是0x30009 ( 即 p 指向的地址是0x30009 )
指针的地址:p 所占据的内存区 (即 p 本身的地址为0x30001)
所以要注意:指针的值 和 指针的地址 不是同一个东西,一个代表 p ,另一个代表&p 。
二、为什么使用指针、什么情况下使用指针
1、函数之间无法通过传参共享变量。
函数的形参变量属于被调用于者,实参属于调用者,函数之间的变量名字空间相互独立是可以重名的,函数之间的数据传递都是值传递(赋值、内存拷贝)。
2、使用指针可以优化函数之间传参的效率。(无需传递数组中所有参数,直接传递数组首地址)
3、堆内存无法与标识符建立联系,只能配合指针使用。
三、如何使用指针
? 定义:类型 * 变量名p;
1、指针变量与普通变量使用方法有很大区别,一般以 p 结尾,与普通变量区分开。
2、* 表示此变量是指针变量,一个 * 只能定义出一个指针变量,不能连续定义。
int* p1,p2,p3;
int *p1,*p2,*p3;
3、类型表示的是存储是什么类型变量的地址,它决定当通过地址访问这块内存时访问的字节数。
4、指针变量的默认值也是不确定,一般初始化为NULL(空指针)。
? 赋值操作:指针变量 = 地址
栈地址赋值:
int num = 0;
int* p = NULL;
p = #
堆地址赋值:
int* p = NULL;
p = malloc(4);
? 解引用(根据地址访问内存): * 指针变量名 <=> 变量
1、根据变量中存储的内存编号(即地址)去访问内存中的数据,访问多少个字节要根据指针变量的类型。
2、如果指针变量中存储的地址出错,此时可能发生段错误(这是赋值产生的错误)。
四、使用指针要注意的问题
? 空指针:
1、指针变量的值为NULL(大多数是0,也有特殊情况是1),这种指针变量叫空指针,空指针不能进行解引用(* 指针变量),NULL被操作系统当作是复位地址(存储了系统重启所需要的数据),当操作系统察觉到程序访问NULL位置的数以的时就会向程序发送段错误的信号,程序就会死亡。
2、空指针还被当作错误标志,如果一个函数的返回值是指针类型,实际返回的值是NULL,则说明函数执行失败或出错。
在C语言代码中应该杜绝对空指针进行解引用,当使用来历不明的指针(调用者提供的)前应该先判断是否为NULL。
void func(int* p)
{
if(NULL == p)
{
}
}
? 野指针:指针变量的值是不确定的或都是无效的,这种指针叫野指针。
使用野指针不一定会出问题,可能产生的后果如下
1、一切正常
2、段错误
3、脏数据
五、指针与数组的关系
数组名就是个指针(常指针),数组名与数组首地址是映射关系,而指针是指向关系。
由于数组名就是指针,所以数组名可以使用指针的解引用运算符,而指针也可以使用数组的 [ ] 运算符。
注意:使用数组当函数的参数时,数组会蜕变成指针,长度也就丢失,因此需要额外添加一个参数用来传递数组的长度。
六、指针的运算
指针的本质就是个整数,因此从语法上来说整数能使用的运算符它都能使用。
- 不是所有的运算符对指针运算都是有意义的。
指针+ 整数 <=> 指针+ 宽度* 整数 向右移动
指针 - 整数 <=> 指针 - 宽度* 整数 向左移动
指针 - 指针 <=> 指针 - 指针/宽度 计算出两个指针之间相隔多少个元素。
七、指针与const配合
const int* p; 保护指针指向的数据,不能通过指针解引用修改指针指向的内存地址中的值。
int const *p;
int * const p; 保护指针变量,指针变量(地址)初始化之后不能再显式的赋值,即指针的值不能被显示的修改。
const int *const p; 既不能修改指针的值,也不能修改指针指向的内存地址中的值。
int const * const p;
八、什么是二级指针,什么情况下使用
二级指针:指向指针的指针。
一级指针与二级指针的比较:一级指针的值为地址,该值(即地址)需要空间来存放,存放在指针的地址中,是空间就具有地址,二级指针就是为了获取这一空间的地址。一级指针所关联的是其值(一个地址)名下空间里的数据,这个数据可以是任意类型并做任意用途,但二级指针所关联的数据只有一个类型一个用途,就是地址。
一级指针(图中 p): 其指针的值是一个变量的地址(a的地址),对其解引用得到的是一级指针所指向变量的值(a的值10086)。
二级指针 (图中 pp):其指针的值是一个指针的地址(p的地址),对其解引用得到的是二级指针所指向的指针的值(p的值0x100A2)。
指针的用途:提供目标的读取或改写,而二级指针就是对于内存地址的读取和改写。
二级指针作为函数参数的作用:在函数外部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效,那么我们就需要二级指针。
九、指针函数和函数指针
函数具有可赋值给指针的物理内存地址,一个函数的函数名就是一个指针,它指向函数的代码。一个函数的地址是该函数的进入点,也是调用函数的地址。函数的调用可以通过函数名,也可以通过指向函数的指针来调用。函数指针还允许将函数作为变元传递给其他函数。
指针函数:简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
声明格式为:类型标识符 *函数名(参数表)
int fun(int x,int y);
这种函数应该都很熟悉,其实就是一个函数,然后返回值是一个 int 类型,是一个数值。
int * fun(int x,int y);
这和上面那个函数唯一的区别就是在函数名前面多了一个*号,而这个函数就是一个指针函数。
其返回值是一个 int 类型的指针,是一个地址。
这样描述应该很容易理解了,所谓的指针函数也没什么特别的,和普通函数对比不过就是其返回了一个指针(即地址值)而已。
函数指针:其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
声明格式:类型说明符 (*函数名) (参数)
int (*fun)(int x,int y);
函数指针是需要把一个函数的地址赋值给它,有两种写法:
fun = &Function;
fun = Function;
取地址运算符&不是必需的,因为一个函数标识符就表示了它的地址,
如果是函数调用,还必须包含一个圆括号括起来的参数表。
调用函数指针的方式也有两种:
x = (*fun)();
x = fun();
两种方式均可,其中第二种看上去和普通的函数调用没啥区别,如果可以的话,建议使用第一种,因为可以清楚的指明这是通过指针的方式来调用函数。当然,也要看个人习惯,如果理解其定义,随便怎么用都行啦。
十、数组指针和指针数组
数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4];
p=a;
p++;
所以数组指针也称指向一维数组的指针,亦称行指针。
指针数组
定义 int * p[n];
[]优先级高,先与p结合成为一个数组,再有int* 说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 * p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
p++;
for(i=0;i<3;i++)
p[i]=a[i]
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]所以要分别赋值。
这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
优先级:()>[]>*
(近期更新中…)
十一、结构体指针
十二、结构体成员指针
十三、指针与堆内存配合
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)