接下来,通过示例彻底理解自增运算符的两种用法(自减的用法与之类似,只不过是加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. Docker中使用Nginx镜像配置HTTPS和HTTP强制使用HTTPS访问(4)

    一.前言 上一文章当中说了Docker-Compose管理镜像和容器,本文章介绍使用Docker中Nginx镜像,使用的工具和ubuntu版本在ASP.NET CORE部署在Docker容器中已详细说 ...

  2. pnpm 中无法使用 patch-package 打补丁

    原文:https://lwebapp.com/zh/post/pnpm-patch-package 介绍 前端开发过程中,经常会遇到第三方开源库有 BUG 的情况,通常我们有以下处理方式 自己 for ...

  3. 如何简化跨网络安全域的文件发送流程,大幅降低IT人员工作量?

    为什么要做安全域的隔离? 随着企业数字化转型的逐步深入,企业投入了大量资源进行信息系统建设,信息化程度日益提升.在这一过程中,企业也越来越重视核心数据资产的保护,数据资产的安全防护成为企业面临的重大挑 ...

  4. Prowide Core:SWIFT [tm] MT(FIN)消息传递的Java框架 开源

    Prowide Core(以前称为WIFE)是用于管理SWIFT MT消息的开源Java框架. 库的主要功能是针对所有MT消息类型的全面Java模型,以及简单易懂的解析和构建API. 该项目自2006 ...

  5. SQLServer错误:expected a simple type, a tuple or a list

    dict_data = { 'request_id': request_id, 'original_id': request_body.original_id, 'unit_type': reques ...

  6. SSM整合【狂神说】

    1.环境要求 IDEA MySQL 5.7 Tomcat 9 Maven 3.6 要求: 需要熟练掌握MySQL数据库,Spring,Java Web及Mybatis知识,简单的前端知识. 2.数据库 ...

  7. docker配置文件模板

    { "registry-mirrors": [ "https://bxsfpjcb.mirror.aliyuncs.com" ], "max-conc ...

  8. ASP.NET Core http请求内容过大, IIS服务器 返回 Request Too Long 解决方案

    1.修改web.config文件内容如下: <?xml version="1.0" encoding="utf-8"?> <configura ...

  9. Oracle查询表中的各列的列名,数据类型,以及类型长度

    SELECT table_name, column_name, data_type,data_length FROM all_tab_cols WHERE table_name = UPPER('ba ...

  10. elementui树状结构添加右键点击事件

    <el-tree :highlight-current="highlight" :data="folderList" :props="defau ...