指针和指针运算符一起时的运算规则(比如*p++和*++p的区别)

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

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

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

[cpp] view
plain
copy

  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. }

例子输出结果:

[cpp] view
plain
copy

  1. a = 9, b = 11

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

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

[cpp] view
plain
copy

  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. }

例子输出结果:

[cpp] view
plain
copy

  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:

[cpp] view
plain
copy

  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. }

例子输出结果:

[cpp] view
plain
copy

  1. a = 1, b = 3

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

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

时间: 2024-10-26 21:21:19

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

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

#运算符:用于在预编译期将宏参数转换为字符串 #define CONVERS(x)  #x   //注:没用双引号包括. 不同类型的指针占用的内存空间大小相同. 局部变量 定义: a[5]; 打印a[i]时,a[i]为随机数. 若定义a[5]={1,2}; 打印时,a[0]=1;a[1]=2;a[2]=a[3]=a[4]=0; 数组地址与数组名: 1.数组名代表数组首元素的地址.  a=&a[0]; 2.数组的地址需要用取地址符号&才能得到 (&a) 3.数组首元素的地址值与数组的

链表操作时巧用指针的指针

比如在插入有序链表的过程中,一般情况下要使用俩指针来遍历,而后还要判断是否在第一个位置插入:利用指针的指针后不需要另外考虑这种特殊情况. 代码: #include <stdio.h> #include <stdlib.h> struct node{ int data; struct node *next; } *head; //sorted link-list void insert(struct node *new_node){ struct node *cur = head,

指针参数--指针做参数时应该注意的地方

转自:http://blog.csdn.net/sszgg2006/article/details/9037675 首先看以下程序: [cpp] view plain copy #include <stdio.h> int *swap(int *px, int *py){ int temp; temp = *px; *px = *py; *py = temp; return px; } int main(void){ int i = 10, j = 20; int *p = swap(&

【转】让你不再害怕指针——C指针详解(经典,非常详细)

前言:复杂类型说明 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样,所以我总结了一下其原则:从变量名处起,根据运算符优先级结合,一步一步分析.下面让我们先从简单的类型开始慢慢分析吧: [cpp] view plain copy int p; //这是一个普通的整型变量 int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,

数组指针、指针数组、函数指针、指针函数 -----笔记

1.数组的四种访问方式 定义数组 a[]; 指针 *p 指向数组a; (1) 利用数组的下表进行访问 a[i]; (2) 数组名+地址的偏移量i *(a+i) (3) 利用指针 p 进行下表访问 p[i] (4) 指针p + 地址的偏移量i *(p+i) 一维数组数组名:相当于一个单指针 2. 数组指针(指针)     指针数组(数组) 函数指针(指针)     指针函数(函数)    -------->只看后边两个字就能够区分是指针还是数组或函数 _______________________

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

1.数组跟指针的区别 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建.数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变. 指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存.指针远比数组灵活,但也更危险. 数组和指针特点的简单比较: 数组 指针 保存数据 保存地址 直接访问数据 间接访问数据,先取得指针的内容,然后以它为地址取得数据 用于存储数目固定且类型相同的数据 通常用于动态数据结构 编译器自动分配和删除

深入理解 [指针函数] 、[函数指针]、[指针的指针]、[指向指针数组的指针]

指针函数 1.指针函数是指带指针的函数,即本质是一个函数.当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需要指针或地址的表达式中. 函数返回类型是某一类型的指针: 格式: 类型标识符  *函数名(参数表) int *match(void *key_x,void *key_y); 解析:首先来说它是一个函数,只不过这个函数的返回值是一个地址值.函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针

常量指针与指针常量的区别(转帖)

三个名词虽然非常绕嘴,不过说的非常准确.用中国话的语义分析就可以很方便地把三个概念区分开. 一) 常量指针. 常量是形容词,指针是名词,以指针为中心的一个偏正结构短语.这样看,常量指针本质是指针,常量修饰它,表示这个指针乃是一个指向常量的指针(变量). 指针指向的对象是常量,那么这个对象不能被更改. 在C/C++中,常量指针是这样声明的: 1)const int *p; 2)int const *p; 常量指针的使用要注意,指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改,也

函数指针与指针函数

一. 在学习arm过程中发现这“指针函数”与“函数指针”容易搞错,所以今天,我自己想一次把它搞清楚,找了一些资料,首先它们之间的定义: 1.指针函数是指带指针的函数,即本质是一个函数.函数返回类型是某一类型的指针 类型标识符    *函数名(参数表) int *f(x,y); 首先它是一个函数,只不过这个函数的返回值是一个地址值.函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针变量. 表示: float *fun();