C语言:指针

指针

  指针是 C 语言最重要的(有时也是最复杂的)概念之一。一些任务,如动态内存分配,没有指针是无法执行的。

  指针,用于储存变量的地址。前面使用的scanf()函数中就使用地址作为参数。概括地说,如果主调函数不使用return返回的值,则必须通过地址才能修改主调函数中的值。

声明

type *name;

  要创建指针变量,先要声明指针变量的类型。如,以下声明:

int * pi;
char * pi;

  类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。

  声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。

间接运算符 *

  * 给出储存在指针指向地址上的值,所以 *有时候也叫解运算符。

  * 和指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格。

  pi的值是一个地址,在大部分系统内部,该地址由一个无符号整数表示。但是,不要把指针认为是整数类型。一些处理整数的操作不能用来处理指针,反之亦然。所以,指针实际上是一个新类型,不是整数类型。因此,ANSI C专门为指针提供了%p格式的转换说明。

一元运算符&

  一元&运算符给出变量的存储地址。例如,存在变量exp,那么&exp就是这个变量的地址(变量在内存中的位置)。如下:ptr为指向nurse的指针。

ptr = &nurse; 

如果配合间接运算符使用,可以实现赋值,交换值,函数中改变值等操作。

nurse = 22;
ptr = &nurse; // 指向nurse的指针
val = *ptr; // 把ptr指向的地址上的值赋给val

指针操作

  • 赋值:可以把地址赋给指针。

  • 解引用:*运算符给出指针指向地址上储存的值。

  • 取址:和所有变量一样,指针变量也有自己的地址和值。对指针而言,&运算符给出指针本身的地址。例如,ptr本身是指向nurse的指针,&ptr则是指向指针的指针。

  • 指针与整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。。如果相加的结果超出了初始指针指向的数组范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效。

  • 递增(递减)指针:递增指向数组元素的指针可以让该指针移动至数组的下一个元素。

  • 指针减去(加上)一个整数:可以使用-运算符从一个指针中减去一个整数。指针必须是第1个运算对象,整数是第 2 个运算对象。该整数将乘以指针指向类型的大小(以字节为单位),然后用初始地址减去乘积。

  • 指针求差:可以计算两个指针的差值。通常,求差的两个指针分别指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。只要两个指针都指向相同的数组(或者其中一个指针指向数组后面的第 1 个地址),C 都能保证相减运算有效。如果指向两个不同数组的指针进行求差运算可能会得出一个值,或者导致运行时错误。 注意,这里的减法有两种。可以用一个指针减去另一个指针得到一个整数,或者用一个指针减去一个整数得到另一个指针。

  • 比较:使用关系运算符可以比较两个指针的值,前提是两个指针都指向相同类型的对象。

解引用指针注意点

  • 可以解引用指向数组任意元素的指针。但是,即使指针指向数组后面一个位置是有效的,也能解引用这样的越界指针。
  • 千万不要解引用未初始化的指针。创建一个指针时,系统只分配了储存指针本身的内存,并未分配储存数据的内存。因此,在使用指针之前,必须先用已分配的地址初始化它。

指向数组的指针

  指针能有效地处理数组。数组表示法其实是在变相地使用指针。

  假设sz是一个数组,那么有:

sz == &sz[0];

  sz和&sz[0]都表示数组首元素的内存地址。两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值。

  还可以使用指针标识数组的元素和获得元素的值。

dates + 2 == &date[2] // 相同的地址
*(dates + 2) == dates[2] // 相同的值

  所以,一旦把第一个元素的地址存储在指针p 中,您就可以使用 * p、* (p+1)、*(p+2) 等来访问数组元素。

保护数组中的数据

   编写一个处理基本类型(如,int)的函数时,要选择是传递int类型的值还是传递指向int的指针。对于数组别无选择,必须传递指针,因为这样做效率高。如果一个函数按值传递数组,则必须分配足够的空间来储存原数组的副本,然后把原数组所有的数据拷贝至新的数组中。如果把数组的地址传递给函数,让函数直接处理原数组则效率要高。

  C 通常都按值传递数据,因为这样做可以保证数据的完整性。如果函数使用的是原始数据的副本,就不会意外修改原始数据。但是,处理数组的函数通常都需要使用原始数据,因此这样的函数可以修改原数组。

  如果使用函数不是为了修改数组中的值,那么在函数原型和函数定义中声明形式参数时应使用关键字const。,这样使用const并不是要求原数组是常量,而是该函数在处理数组时将其视为常量,不可更改。这样使用const可以保护数组的数据不被修改。

const和指针赋值规则

  • 把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
const double * pc = rates; // 有效
pc = locked; //有效
  • 只能把非const数据的地址赋给普通指针。否则,通过指针就能改变const数组中的数据。
  • 可以声明并初始化一个不能指向别处的指针,关键是const的位置:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
double * const pc = rates; // pc指向数组的开始
pc = &rates[2]; // 不允许,因为该指针不能指向别处
*pc = 92.99; // 没问题 -- 更改rates[0]的值

可以用这种指针修改它所指向的值,但是它只能指向初始化时设置的地址。

  • 在创建指针时还可以使用const两次,该指针既不能更改它所指向的地址,也不能修改指向地址上的值:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * const pc = rates;
pc = &rates[2]; //不允许
*pc = 92.99; //不允许
  • 使用非const标识符(如,mult_arry()的形参ar)修改const数据(如,locked)导致的结果是未定义的。
void mult_array(double ar[], int n, double mult)
{
...
}
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
mult_array(rates, 5, 1.2); // 有效
mult_array(locked, 4, 1.2); // 不要这样做
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值