C语言中&运算符和*运算符详解

C语言中&运算符和*运算符详解


前言

在学习C Primer Plus 数组与指针一章时,对取址运算符& 与间接运算符* 的使用,始终有些迷惑,直到又结合左值与右值进行了一次深入的学习,感觉有些清楚了,写篇文章总结一下。


提示:以下是本篇文章正文内容

一、取址运算符&与间接运算符*

1.取址运算符&

取值运算符& 是用来取得其操作对象的地址。如果操作对象x的类型为T,则表达式&x的类型是T类型指针(指向T类型对象 x的指针)。
取值运算符的操作对象必须是在内存中可寻址到的地址。换句话说,该运算符只能用于函数或对象(左值),而不可用于位字段,以及哪些还未被存储类修饰符register声明的内容。
当需要初始化指针,以指向某些对象或函数时,需要获得这些对象或函数的地址:

float x, *ptr;
ptr = &x;  // 合法,使得指针ptr指向x
ptr = &(x+1);  // 错误,(x+1)不是一个左值

2.间接运算符*

与取址运算符& 相反,间接运算符* 用于当已经具有一个指针,并希望获取它所引用的对象时,因此也被称为解引用运算符
它的操作对象必须是指针类型。如果ptr是指针,那么 *ptr就是
ptr所指向的对象或函数。如果ptr是一个对象指针,那么 *ptr就是一个左值,可以把它即( *ptr)当做赋值运算符左边的操作数:

float x, *ptr = &x;
*ptr = 1.7;  // 将1.7赋值给变量x
++(*ptr);  // 并将变量x的值加1

在最后的语句中,ptr不变,但是变量x的值变为了2.7。

注意:千万不要解引用未初始化的指针!!!

int * ptr;  // 未初始化的指针
*pt = 5;  // 严重错误!!!

因为,ptr还未被初始化,其值是一个随机值,所以不知道5将储存在何处,可能不会出什么错,但是也可能擦写数据或代码,或者将导致程序崩溃,切记,创建一个指针时,系统只分配了储存指针本身的内存,并未分配储存数据的内存。 在使用指针前,务必先用已分配的地址初始化它,例如使用现有变量的地址初始化,或者使用malloc()函数分配内存。

3.优先级与结合律

像其他一元运算符一样,&运算符和*运算符有很高的优先级,结合律是从右向左。
例如:

int urn[5] = {100, 200, 300, 400, 500};
int * start;
start = urn;

total += *start++;  // 使用后缀递增
total += *++start;  // 使用前缀递增
total += (*start)++  // 解引用后递增

设想一下这三种形式的结果分别是什么?
一元运算符*和++的优先级相同,但是结合律是从右向左,所以:

第一种情况:先把指针指向位置上的值加到total上,然后再递增指针。
第二种情况:先递增指针,指向数组的下一个元素,在把指针指向的当前位置的值加到total上。
第三种情况:先解引用,获取指针指向位置上的值,在递增该值,在把递增后的值加到total上,指针将一直指向同一个位置,只是该位置上的值发生了变化。

运算符&和*是互补的:如果x是一个表达式,用于指定一个对象或一个函数,那么表达式 *&x就等于x。相反地,在形如 &*ptr的表达式中,这些运算符会互相抵消,表达式的类型与值等效于ptr。然而,不管ptr是不是左值,&*ptr都一定不会是左值。

二、左值与右值

左值(lvalue)是用来指明一个对象的表达式。最简单的左值就是变量名称。左值之所以称为“左”,是因为一个左值表示一个对象,它可以出现在赋值运算符的左边,例如“=”。
其他的表达式(那些能表示一个值但不指明一个对象的),就类似地称为右值(rvalue)。右值可以出现在赋值运算符的右边而不是左边的表达式。例如常量和算术表达式。
从一个左值中必定可以解析出对应对象的地址,除非该对象是位字段或者被声明为寄存器存储类。生成左值的运算符包括下标运算符[]和间接运算符*。
因此,上面说到的&*ptr必定不是左值。

由此可以总结一下,指针和数组表达式哪些可能是左值:

表达式是左值吗
array[1]是;一个数组元素是一个具有位置的对象
&array[1]否;数组元素对象的地址,不是一个具有位置的对象
ptr是;指针变量是一个具有位置的对象
*ptr是;指针元素所指的地址是一个具有位置的对象
ptr+1否;指针的加法,产生一个新的地址,但是不能保证是一个对象
*ptr+1否;此加法产生的是一个新的算术值,但不是一个对象

对象可以被声明为const常量,但是在这种情况下,该对象就不能位于赋值运算符的左边,尽管它是左值:

int a = 1;
const int b = 2, *ptr = &a;
b = 20;  // 错误,b是const int
*ptr = 10;  // 错误,ptr是const int指针

赋值运算左边的操作数,以及任何自增或自减运算符(++和–)的操作数,不仅应该是左值,还应该是可修改的左值。可修改的左值不能声明 限定符const,并且可修改的左值不能是数组类型。 如果可修改的左值所表示的对象时结构或联合类型,那么它的元素都不可以被声明(不管是直接或者间接地)为具有限定符const的类型。

三、总结

经过了两个运算符、左值及右值的学习,我认识到,数组和指针的学习中,除了要对指针、变量、地址、值有深刻的认识外,更要重视左值与右值的区别,在实际的使用中,不要非左值写在运算符的左侧,防止代码错误的发生。

See you next time!

  • 91
    点赞
  • 455
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值