接下来,通过示例彻底理解自增运算符的两种用法(自减的用法与之类似,只不过是加1变成了减1)。

1、++i和i++的区别

如清单1(注意代码中的注释):

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int a, b, i = 7;
  5. i++; //等价于i = i + 1;
  6. ++i; //等价于i = i + 1;
  7. a = i++; //等价于a = i; i = i + 1;
  8. b = ++i; //等价于i = i + 1; b = i;
  9. printf("a = %d, b = %d\n", a, b);
  10. return 0;
  11. }

例子输出结果:

  1. a = 9, b = 11

在例子中,第7和第8行的作用一样,仅仅是为变量i加1,这时i的值已经增加为9,接下来第10行变量a先获得i的值(即9),然后i加1,第11行变量i先再加1,然后把得到的值赋给b,所以b的值为11。

稍微复杂的例子,如清单2:

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int a = 5;
  5. int *p = &a;
  6. int b = (*p)++; //等价于b = a++; 即b = a; a = a + 1;
  7. int c = ++(*p); //等价于c = ++a; 即a = a + 1; c = a;
  8. printf("b = %d, c = %d\n", b, c);
  9. printf("(*p)++ = %d, ++(*p) = %d\n", (*p)++, ++(*p));
  10. return 0;
  11. }

例子输出结果:

  1. b = 5, c = 7
  2. (*p)++ = 8, ++(*p) = 8

在这个例子中,只不过是通过*p来间接地操作a,其他关于自增运算符的用法与清单1类似。第9行的*p一定要用小括号括起来,否则含义就不一样了。而第11行的++(*p)也可以写成++*p(用GCC验证过),那是因为对操作数p来说它只有一个运算符*在计算它,所以无关乎运算符优先级和结合性的问题。

值得注意的是,由于C语言没有指定函数各参数的求值顺序,所以第15行的代码是不可移植的,用不同的编译器可能会产生不同的结果(对于这个例子,GCC是先计算++(*p),后计算(*p)++,所以两者都等于8)。

知识点:

(1)、副作用

在对表达式求值的同时,修改了某些变量的值,其中修改值的行为在C语言中被叫作副作用,那是因为对C语言而言,计算的目的就是对表达式求值,如语句int a = 5,它的含义是先求值得到5,然后把5赋值给变量a,后一步的赋值就是此表达式的副作用。自增和自减运算符就是因为副作用而被使用,除了加1或减1之外,还给自身赋值。

(2)、运算符的优先级

在C语言中,把运算符的优先级分为15级,如下表,从上到下,依次为从最高优先级到最低优先级(为了方便记忆,将15级分成11类,并对每类进行了命名)。

初等运算符

包括小括号 ()、中括号 [] 、成员访问运算符 . 和 -> 。

一元运算符

包括自增++和自减--、正负号+ 和-、间接运算*和取址运算& 、类型转换(type)、 sizeof 、逻辑反! 、位取反~等。

算术运算符

包括两级,先乘除(*、/、%)后加减(+、-)。

位移运算符

包括左移 << 和右移 >> 。

关系运算符

包括小于 < 、小于等于 <= 、大于 > 、大于等于 >= 。

判等运算符

包括相等 == 和不相等 != 。

位逻辑运算符

分三级,依次为位与 &、位异或 ^ 和位或 | 。

逻辑运算符

分两级,依次为逻辑与 && 和逻辑或 || 。

条件运算符

? :

赋值运算符

包括= 、+= 、-=、 *=、 /=、 %= 、&= 、^=、 |= 、<<= 、>>= 。

逗号运算符

(3)、结合性

对于同一操作数,在具有两个相同优先级的操作符时决定先执行哪个操作符的问题就是由结合性决定的。

相同优先级的操作符具有同样的结合性。右结合性就是说表达式中最右边的操作最先执行,然后从右到左依次执行。在C语言中,具有右结合性的操作符只有相应的三类,分别为一元运算符、条件运算符和赋值运算符。

注意:C语言中的优先级和结合性都是针对同一操作数而言的。如表达式24/8*2,对于操作数8而言,/ 和*的优先级相同,所以再根据它们的左结合性可知,表达式是先计算24/8得到3,然后计算3*2得到6。

C语言并没有规定同一运算符相关的多个操作数的计算顺序(&&、|| 、? : 和 , 运算符除外),如式子a = 8 * 9 + 20 * 4,对操作数9和20而言,根据优先级就可判断先乘后加,但表达式中的两个*并不共享同一操作数,所以从左到右的结合性并不适用它,8 * 9 和20 * 4的计算顺序是不定的,到底先计算8 * 9还是20 * 4由编译器决定。

在上面例子中,8 * 9和20 * 4谁先执行都不影响最后结果的一致,但有些情况下就未必了,如“ b = 3; a = (b++) * (b++); ”这样的例子,对于不同的编译器最后a的值可能等于9,也可能等于12,甚至可能等于16。因此,在实际应用中不能出现这样的未确定性,根据自己的需要,可以把它改成类似“b = 3; c = b++; a = c * c;”这样的形式。

2、*p++和*++p的区别

举例,如清单3:

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int arr[] = {1, 2, 3, 4};
  5. int *p = arr;
  6. int a = *p++; //等价于a = *(p++); 即a = *p; p = p + 1;
  7. int b = *++p; //等价于b = *(++p); 即p = p + 1; b = *p;
  8. printf("a = %d, b = %d\n", a, b);
  9. return 0;
  10. }

例子输出结果:

  1. a = 1, b = 3

对于第8行的操作数p而言,*和++的优先级相同,但根据它们的右结合性可知,在这个表达式里可认为++的优先级高于*,即*p++等价于*(p++)。

而对于第10行的操作数p而言,它只有一个运算符++,所以先计算++p得出结果,然后间接运算。

指针和指针运算符一起时的运算规则(比如*p++和*++p的区别)的更多相关文章

  1. #运算符、不同的指针类型、数组和指针、指针运算、堆、栈、静态区、只读区、下标VS指针

    #运算符:用于在预编译期将宏参数转换为字符串 #define CONVERS(x)  #x   //注:没用双引号包括. 不同类型的指针占用的内存空间大小相同. 局部变量 定义: a[5]; 打印a[ ...

  2. vs2010修改状态栏的CStatusBar指针的的SetPaneText()方法时死活不对问题

    vs2010的mfc在有些地方不太一样不容易注意到,今天在修改状态栏的时候,就碰见了问题,死活修改不了. 参照下面的帖子: 点击打开链接 : 使用VS2010更改MFC程序的状态栏 2011-04-1 ...

  3. cursor CSS属性定义鼠标指针悬浮在元素上时的外观。

    1 1 cursor CSS属性定义鼠标指针悬浮在元素上时的外观. https://developer.mozilla.org/zh-CN/docs/Web/CSS/cursor 概述 cursor  ...

  4. C语言----变量及作用域 、 指针 、 指针和数组 、 进程空间 、 字符串

    1 使用程序来模拟放球.取球的问题 1.1 问题 栈是一种特殊的线性表,它的逻辑结构和线性表相同,只是其运算规则较线性表有更多的限制,故又称为运算受限的线性表. 栈的定义是限制仅在表的一端进行插入和删 ...

  5. 指针的指针&指向指针数组的指针

    一.指针的指针    指针的指针看上去有些令人费解.它们的声明有两个星号.例如:        char ** cp;    如果有三个星号,那就是指针的指针的指针,四个星号就是指针的指针的指针的指针 ...

  6. 娓娓道来c指针 (3)指针和数组

    (3)指针和数组 在c中指针和数组似乎有着千丝万缕的关系.事实上它们不是一回事:指针是指针,数组是数组.两者不同样. 说它们有关系,只是是由于常见这种代码: int main() { int arra ...

  7. c/c++ 复习基础要点01-const指针、指针函数 函数指针、new/delete与malloc/free区别与联系

    1.      引用本身是有指针实现的:引用为只读指针 例子: int d=123; int& e=d;    //引用 int * const e=d; //只读指针,e指向d,不可修改e指 ...

  8. (C/C++)区别:数组与指针,指针与引用

    1.数组跟指针的区别 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建.数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变. 指针可以随时指向任意类型 ...

  9. [C++]数组指针与指针数组

    //声明: 1.&----取地址运算符 eg: int m = 1; int *p = &m;//(*p) == m的地址 == &m; 2.*----间接访问运算符 eg: ...

  10. C语言学习笔记 (001) - 常量指针与指针常量的区别(转帖)

    三个名词虽然非常绕嘴,不过说的非常准确.用中国话的语义分析就可以很方便地把三个概念区分开. 一) 常量指针. 常量是形容词,指针是名词,以指针为中心的一个偏正结构短语.这样看,常量指针本质是指针,常量 ...

随机推荐

  1. 第六周作业-N67044-张铭扬

    1. 简述DDL,DML,DCL,DQL,并且说明mysql各个关键字查询时候的先后顺序 DDL:Data Defination Language 数据定义语言,主要是建表.删除表.修改表字段等操作 ...

  2. 179. 最大数 (Medium)

    问题描述 179. 最大数 (Medium) 给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数. 注意: 输出结果可能非常大,所以你需要返回一个字符串而不是整数 ...

  3. R包本地安装方法

    问题 需要安装一个R的测试包 但是这个包没有在cran中 也没有在biomanager中 于是决定使用devtools-github安装 遇到问题 无法下载github上的R包 策略 使用local方 ...

  4. Bug的分类及优先级划分

    P0等级(功能无法正常使用.Block测试流程) 严重花屏 内存泄漏 用户数据丢失或破坏 系统崩溃/死机/冻结 模块无法启动或异常退出 严重的数值计算错误 功能设计与需求严重不符 其它导致无法测试的错 ...

  5. S-HR常用源码

    1.public static String getUserId(Context ctx) {        UserInfo userInfo = ContextUtil.getCurrentUse ...

  6. nginx 与 k8s ingress 配置转发websocket

    环境 10.1.100.10:70 是后端websocket 服务 需要通过nginx 向后端转发,nginx 配置文件如下 # cat test-ue4.conf map $http_upgrade ...

  7. ERROR StatusLogger No Log4j 2 configuration file found

    ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only er ...

  8. CodeGym自学笔记03——变量、数据类型

    变量 变量是用来存储数据的特殊实体. 1.在 Java 语言中,所有数据都存储在变量中. 2.最接近的比喻就好比是一个盒子. 3.在 Java 语言中,每个变量都有三个重要的属性:类型.名称和值. - ...

  9. js获取的 后端的列表。

    1.引号被转码 处理办法 {% autoescape off %} var tmp = '{{ data1 }}'; var tmp = '{{ data2 }}'; {% endautoescape ...

  10. springboot mybatisPlus集成shiro实现权限控制

    创建数据库表.由于时间仓促,数据库表设计不太合理,后期会更改 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ------------------- ...