🍌指针
2021-1-22
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property
 

 
什么是指针?
从根本上看,指针(pointer)是一个值为内存地址的变量(或数据对象)。正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址
假设一个指针变量名是ptr,可以编写如下语句:
对于这条语句,我们说ptr“指向”pooh。ptr&pooh的区别是ptr是变量,而&pooh是常量。或者,ptr是可修改的左值,而&pooh是右值。还可以把ptr指向别处:
 
 

间接运算符:*

假设已知ptr指向bah:
然后使用间接运算符*找出储存在bah中的值,该运算符有时也称为解引用运算符。
语句ptr = &bah;val = *ptr;放在一起相当于下面的语句:
 

声明指针

声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。另外,程序必须知道储存在指定地址上的数据类型。long和float可能占用相同的存储空间,但是它们储存数字却大相径庭。
类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。int * pi;声明的意思是pi是一个指针,*piint类型。
*和指针名之间的空格可有可无。通常在声明时使用空格,在解引用变量时省略空格。
 
pc指向的值(*pc)是char类型。pc本身是什么类型?
我们描述它的类型是“指向char类型的指针”。pc 的值是一个地址,在大部分系统内部,该地址由一个无符号整数表示。但是,不要把指针认为是整数类型。一些处理整数的操作不能用来处理指针,反之亦然。例如,可以把两个整数相乘,但是不能把两个指针相乘。所以,指针实际上是一个新类型,不是整数类型。因此,如前所述,ANSI C专门为指针提供了%p格式的转换说明。
 

指针和数组

数组表示法其实是在变相地使用指针,数组名是数组首元素的地址。
flizny&flizny[0]都表示数组首元素的内存地址。两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值。
第2行打印的是两个数组开始的地址,下一行打印的是指针加1后的地址,以此类推。注意,地址是十六进制的。
 
我们的系统中,地址按字节编址,short类型占用2字节,double类型占用8字节。在C中,指针加1指的是增加一个存储单元。对数组而言,这意味着把加1后的地址是下一个元素的地址,而不是下一个字节的地址。这是为什么必须声明指针所指向对象类型的原因之一。只知道地址不够,计算机要知道储存对象需要多少字节(即使指针指向的是标量变量,也要知道变量的类型,否则*pt就无法正确取回地址上的值)。
notion image
数组和指针的关系十分密切,可以使用指针标识数组的元素和获得元素的值。从本质上看,同一个对象有两种表示法。实际上,C语言标准在描述数组表示法时确实借助了指针。也就是说,定义ar[n]的意思是*(ar + n)。可以认为*(ar + n)的意思是“到内存的ar位置,然后移动n个单元,检索储存在那里的值”。
 
不要混淆 *(dates+2)*dates+2
 
 
 

指针操作

C提供了一些基本的指针操作,下面的程序中演示了8种不同的操作:
无论如何,使用指针时一定要注意,不要解引用未初始化的指针!
 
 
 
 

函数、数组和指针

假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为marbles的int类型数组。应该如何调用该函数?也许是下面这样:
那么,该函数的原型是什么?数组名是该数组首元素的地址,所以实际参数marbles是一个储存int类型值的地址,应把它赋给一个指针形式参数,即该形参是一个指向int的指针:
sum()从该参数获得了什么信息?它获得了该数组首元素的地址,知道要在该位置上找出一个整数。注意,该参数并未包含数组元素个数的信息。
有两种方法让函数获得这一信息。第一种方法是,在函数代码中写上固定的数组大小:
另一个比较灵活的方法是把数组大小作为第2个参数:
 
关于函数的形参,还有一点要注意。只有在函数原型或函数定义头中,才可以用int ar[]代替int * ar
int *ar形式和int ar[]形式都表示ar是一个指向int的指针。但是,int ar[]只能用于声明形式参数。第2种形式(int ar[])提醒我们指针ar指向的不仅仅一个int类型值,还是一个int类型数组的元素。
因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参数是一个与之匹配的指针。只有在这种情况下,C才会把int ar[]和int * ar解释成一样。由于函数原型可以省略参数名,所以下面4种原型都是等价的:
 
 
 

使用指针形参

函数要处理数组必须知道何时开始、何时结束。sum()函数使用一个指针形参标识数组的开始,用一个整数形参表明待处理数组的元素个数。但是这并不是给函数传递必备信息的唯一方法。还有一种方法是传递两个指针,第1个指针指明数组的开始处,第2个指针指明数组的结束处。
还可以把循环体压缩成一行代码:
一元运算符*++的优先级相同,但结合律是从右往左,所以start++先求值,然后才是*start。也就是说,指针start先递增后指向。使用后缀形式(即start++而不是++start)意味着先把指针指向位置上的值加到total上,然后再递增指针。如果使用*++start,顺序则反过来,先递增指针,再使用指针指向位置上的值。如果使用(start)++,则先使用start指向的值,再递增该值,而不是递增指针。这样,指针将一直指向同一个位置,但是该位置上的值发生了变化。虽然start++的写法比较常用,但是*(start++)这样写更清楚。
 
 
 

指针表示法和数组表示法

处理数组的函数实际上用指针作为参数,但是在编写这样的函数时,可以选择是使用数组表示法还是指针表示法。
C语言,ar[i]*(ar+i)这两个表达式都是等价的。无论ar是数组名还是指针变量,这两个表达式都没问题。但是,只有当ar是指针变量时,才能使用ar++这样的表达式。
 
  • C
  • 数组const
    目录