C语言杂谈(二)自增运算符++与间接访问运算符*的结合关系和应用模式
自增运算符++有前缀和后缀两种,在搭配间接访问运算符*时,因为顺序、括号和结合关系的影响,很容易让人产生误解,产生错误的结果,这篇文章来详细分析一下这几种运算符的不同搭配情况。
++、--和*的优先级顺序
在C语言运算符的优先级顺序中,后缀的++和--运算符运算优先级16,结合关系是从左到右;简介访问运算符*、前缀++和--运算符运算优先级15,结合关系是从右到左。根据这个关系,可以分析出不同情况下的应用。为了更直观的体现,有以下的例子。
举例说明
有数组a[5],值分别为10,11,12,13,14;有指针p指向a[0];另有指针q和整数b。这里注意的是,每段代码开始前,a数组的值和p指针都会回复初始状态。(在一开始写这些测试代码时,忽视了回复初值的问题,导致很多奇怪的错误,比如p已经不再指向a[0],或者a[0]本身已经改变了。)
1.后缀++
p = a;
q = p++;
p指向a[1]值为11,q指向a[0]值为10。说明后缀自增运算符,返回的是自增前的值。
2.前缀++
p = a;
q = ++p;
p指向a[1]值为11,q指向a[1]值为11。说明前缀自增运算符,返回的是自增后的值。
3.*p++
p = a;
b = *p++;
p指向11,b值为10。此时先执行p++,p自增,指向a[1]。p++的值为自增前的值,即a[0]的地址,a[0]的值赋给b。
4.*(p++)
p = a;
b = *(p++);
结果与3相同。*与++运算符优先级相同,运算顺序是自右向左。
5.(*p)++
p = a;
b = (*p)++;
此时虽然结果与3相同,但这时是先取p指向的值即a[0],a[0]自增即a[0]的值变成11,自增是后缀的,返回自增前的值,即10。结果相同,但原理还有很大的差别。
6. *++p
p = a;
b = *++p;
p指向11,b值为11。先执行++p,p自增,指向a[1],++p的值为自增后的值,即a[1]的地址,a[1]的值赋给b。
7.*(++p)
p = a;
b = *(++p);
与6结果相同,*与++运算符优先级相同,运算顺序是自右向左。
8.++(*p)
p = a;
b = ++(*p);
p指向11,b值为11,先取p指向a[0]值为10,a[0]的值自增变成11,前缀自增返回值为11赋给b。
9. ++*p
p = a;
b = ++*p;
结果同8,*与++运算符优先级相同,运算顺序是自右向左。
总结
第一个原则就是前缀值为变化后,后缀值为变化前。第二个原则,*++优先级相同,读的时候从右向左。这些都是本人自己试着总结的,如有错误请多指正,希望对有疑惑的朋友有所帮助。
完整代码
/*本程序用来测试前缀自增运算符,后缀自增运算符,取内容符号的运算优先级以及顺序带来的返回值的影响*/ #include "stdio.h" void myPrint(int n,int j, int k)
{
printf("no.%d:p->%d, q->%d\n", n,j, k);
}
void myPrintNew(int n,int j, int k)
{
printf("no.%d:p->%d, b->%d\n", n,j, k);
}
void init(int a[])
{
a[] = ;
a[] = ;
a[] = ;
a[] = ;
a[] = ;
}//防止某些测试的操作改变了数组的值,在每次使用数组之前使用初始化函数 int main(int argc, char* argv)
{
//int a[5] = { 10, 11, 12, 13, 14 }, *p,*q,b;
int a[] ,*p, *q, b;
init(a); //1.
init(a);
p = a;
q = p++;
myPrint(,*p, *q);
//p指向11,q指向10
//后缀自增运算符,返回的是自增前的值 //2.
init(a);
p = a;
q = ++p;
myPrint(,*p, *q);
//p指向11,q指向11
//前缀自增运算符,返回的是自增后的值
//1,2:阅读顺序:从左到右 //3.
init(a);
p = a;
b = *p++;
myPrintNew(,*p, b);
//p指向11,b值为10
//先执行p++,p自增,指向a[1],p++的值为自增前的值(见1),即a[0]的地址,a[0]的值赋给b //4.
init(a);
p = a;
b = *(p++);
myPrintNew(,*p, b);
//结果与3相同
//*与++运算符优先级相同,运算顺序是自右向左 //5.
init(a);
p = a;
b = (*p)++;
myPrintNew(,*p, b);
//结果与3相同,但这时是先取p指向的值即a[0],a[0]自增即a[0]的值变成11,自增是后缀的,返回自增前的值,即10
//与8对照 //6。
init(a);
p = a;
b = *++p;
myPrintNew(, *p, b);
//p指向11,b值为11
//先执行++p,p自增,指向a[1],++p的值为自增后的值(见2),即a[1]的地址,a[1]的值赋给b //7.
init(a);
p = a;
b = *(++p);
myPrintNew(, *p, b);
//与6结果相同
//*与++运算符优先级相同,运算顺序是自右向左 //
init(a);
p = a;
b = ++(*p);
myPrintNew(, *p, b);
//p指向11,b值为11
//先取p指向a[0]值为10,a[0]的值自增变成11,前缀自增返回值为11赋给b //9??
init(a);
p = a;
b = ++*p;
myPrintNew(, *p, b);
//结果同8
//*与++运算符优先级相同,运算顺序是自右向左 return ;
}
运行结果
补充:
在C++中,建议除非必须,否则不使用后置版本的递增递减运算符。
其原因是前置版本的递增运算符避免了不必要的工作,把加1后的值直接返回改变了运算对象。与之相比,后置版本需要先存储自增前的值,以便返回未修改的值。这是一种浪费。
C语言杂谈(二)自增运算符++与间接访问运算符*的结合关系和应用模式的更多相关文章
- [C]成员运算符"."和间接成员运算符"->"浅析
成员运算符: . 成员运算符一般和结构或者联合名一起使用,指定结构或者联合中的某个成员. 举个栗子: 如果Ronz是一个结构的名称,linux是这个结构模板指定的一个成员名. struct{ //匿名 ...
- Swift5 语言指南(二十八) 高级运算符
除了Basic Operators中描述的运算符之外,Swift还提供了几个执行更复杂值操作的高级运算符.这些包括C和Objective-C中您熟悉的所有按位和位移运算符. 与C中的算术运算符不同,S ...
- Swift5.1 语言指南(二十九)高级运算符
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- C语言 预处理二(宏定义--#define)
//#define 宏定义(宏定义一般大写) //知识点一-->#define的作用域:从#define开始,从上往下,如果遇到#undef就到#undef处结束,如果没有就是作用于当前整个文件 ...
- go语言入门(二)
Go 语言变量 Go 语言变量名由字母.数字.下划线组成,其中首个字母不能为数字. 声明变量的一般形式是使用 var 关键字: var identifier type 变量声明 第一种,指定变量类型, ...
- #r语言(二)笔记
#r语言(二)笔记 #早复习 #概述:R是用于统计分析.绘图的语言和操作环境 #对象: #数据类型--统称为对象 #向量(vector):用于存储数值型.字符型或逻辑型数据的一维数组. #定义向量: ...
- Go 语言 切片的使用(增删改查)
Go 语言 切片的使用(增删改查) 引言Golang 的数组是固定长度,可以容纳相同数据类型的元素的集合.但是当长度固定了,在使用的时候肯定是会带来一些限制,比如说:申请的长度太大会浪费内存,太小又不 ...
- 使用C语言实现二维,三维绘图算法(1)-透视投影
使用C语言实现二维,三维绘图算法(1)-透视投影 ---- 引言---- 每次使用OpenGL或DirectX写三维程序的时候, 都有一种隔靴搔痒的感觉, 对于内部的三维算法的实现不甚了解. 其实想想 ...
- 使用C语言实现二维,三维绘图算法(3)-简单的二维分形
使用C语言实现二维,三维绘图算法(3)-简单的二维分形 ---- 引言---- 每次使用OpenGL或DirectX写三维程序的时候, 都有一种隔靴搔痒的感觉, 对于内部的三维算法的实现不甚了解. 其 ...
随机推荐
- C++ - 多线程的实现
支持多线程可谓是C++语言最大的变化之一. 此前,C++只能利用操作系统的功能(Unix族系统使用pthreads库),或是例如OpenMP和MPI这些代码库,来实现多核计算的目标. C++本身并没有 ...
- 循序渐进开发WinForm项目(4)--Winform界面模块的集成使用
随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...
- GridView如何实现双击行进行编辑,更新
虽然标题是原创,但是其实主要的思想呢还是接见了晓风残月的思路,今天在晓风残月的博客上看到了如何利用GridView来实现双击进行编辑.我决定动手实现一下,由于还没有实现双击进行更改操作,所以顺便就把这 ...
- ASP.NET和MSSQL高性能分页
首先是存储过程,只取出我需要的那段数据,如果页数超过数据总数,自动返回最后一页的纪录: set ANSI_NULLS ON set QUOTED_IDENTIFIER ON GO -- ======= ...
- 用UltraISO制作支持windows 7的U盘启动盘
用UltraISO制作U盘启动盘,有人写过,我也看过,不过依照网上的那些文章,成功的并不多,经过几次试验,在不同的主板环境下成功概率高的方法应该如下: 1. UltraISO建议9.3以上 2. ...
- Python基础:映射(字典)
一.概述 映射类型(Mapping Types)是一种关联式的容器类型,它存储了对象与对象之间的映射关系. 字典(dict)是Python中唯一的映射类型,它是存储了一个个 键值对(由 键 映射到 值 ...
- SQL交叉表
之前做货品横向展示时,有看到评论说用到交叉表. 公司最近需要给订单表做一个数据汇总的功能,同事给到一个参考SQL select * from (select COUNT(1) as 已锁定 from ...
- csharp:Dapper Sample
You can find Dapper on Google Code here: http://code.google.com/p/dapper-dot-net/ and the GitHub dis ...
- 解决Spring MVC @ResponseBody返回html中中文字符串乱码问题
最近有个应用,通过responsebody返回完整的html页面时出现乱码是异常的问题,因为是通过responsebody返回,所以一开始设置了text/plain的字符集,如下: <mvc:a ...
- R语言归一化处理
归一化化就是要把你需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内.首先归一化是为了后面数据处理的方便,其次是保正程序运行时收敛加快. R语言中的归一化函数:scale 数据归一化包括 ...