C/C++:运算符的详解及其运算优先级


运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言拥有丰富的运算符,并提供了以下类型的运算符:

  • 算术运算符
  • 位运算符
  • 关系运算符
  • 逻辑运算符
  • 赋值运算符
  • 其他运算符

本章将逐一介绍算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和其他运算符。

算术运算符

下表显示了 C 语言支持的所有算术运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符描述实例
+把两个操作数相加A + B 将得到 30
-从第一个操作数中减去第二个操作数A - B 将得到 -10
*把两个操作数相乘A * B 将得到 200
/分子除以分母B / A 将得到 2
%取模运算符,整除后的余数B % A 将得到 0
++自增运算符,整数值增加 1A++ 将得到 11
--自减运算符,整数值减少 1A-- 将得到 9

实例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int a = 10;
	int b = 20;
	int c;

	c = a + b;
	printf("Line 1 - c 的值是 %d\n", c);
	c = a - b;
	printf("Line 2 - c 的值是 %d\n", c);
	c = a * b;
	printf("Line 3 - c 的值是 %d\n", c);
	c = b / a;
	printf("Line 4 - c 的值是 %d\n", c);
	c = b % a;
	printf("Line 5 - c 的值是 %d\n", c);
	c = a++;  // 赋值后再加 1 ,c 为 10,a 为 11
	printf("Line 6 - c 的值是 %d, a 的值是 %d\n", c, a);
	c = a--;  // 赋值后再减 1 ,c 为 11 ,a 为 10
	printf("Line 7 - c 的值是 %d, a 的值是 %d\n", c, a);

	system("pause");
	return 0;
}

程序生成图:
在这里插入图片描述

验证 a++ 与 ++a 的区别:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int a = 10;
	int c;

	c = a++;
	printf("先赋值后运算:\n");
	printf("Line 1 - c 的值是 %d, a 的值是 %d\n", c, a);
	a = 10;
	c = a--;
	printf("Line 2 - c 的值是 %d, a 的值是 %d\n", c, a);

	a = 10;
	c = ++a;
	printf("先运算后赋值:\n");
	printf("Line 3 - c 的值是 %d, a 的值是 %d\n", c, a);
	a = 10;
	c = --a;
	printf("Line 4 - c 的值是 %d, a 的值是 %d\n", c, a);

	system("pause");
	return 0;
}

程序生成图:
在这里插入图片描述

位运算符

位运算符作用于位,并逐位执行操作。&(按位与)、 |(按位或) 和 ^(按位异或)、~(按位取反操作符) 的真值表如下所示:

pqp & qp | qp ^ q~p
000001
010111
111100
100110

假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:
A = 0011 1100
B = 0000 1101
则:
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A = 1100 0011

下表为 C 语言支持的移位运算符。假设变量 A 的值为 60,则:

运算符描述实例
<<二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。A << 2 将得到 240,即为 1111 0000
>>二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。A >> 2 将得到 15,即为 0000 1111

实例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	unsigned int a = 60;    /* 60 = 0011 1100 */
	unsigned int b = 13;    /* 13 = 0000 1101 */
	int c = 0;

	c = a & b;       /* 12 = 0000 1100 */
	printf("Line 1 - c 的值是 %d\n", c);
	c = a | b;       /* 61 = 0011 1101 */
	printf("Line 2 - c 的值是 %d\n", c);
	c = a ^ b;       /* 49 = 0011 0001 */
	printf("Line 3 - c 的值是 %d\n", c);
	c = ~a;          /*-61 = 1100 0011 */
	printf("Line 4 - c 的值是 %d\n", c);
	c = a << 2;     /* 240 = 1111 0000 */
	printf("Line 5 - c 的值是 %d\n", c);
	c = a >> 2;     /* 15 = 0000 1111 */
	printf("Line 6 - c 的值是 %d\n", c);

	system("pause");
	return 0;
}

程序生成图:
在这里插入图片描述

对于移位运算符,不要移动负数位,这个是标准未定义的。 例如:

int num = 10;
num>>-1;//error

关系运算符

下表为 C 语言支持的所有关系运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符描述实例
==检查两个操作数的值是否相等,如果相等则条件为真。(A == B) 不为真。
!=检查两个操作数的值是否相等,如果不相等则条件为真。(A != B) 为真。
>检查左操作数的值是否大于右操作数的值,如果是则条件为真。(A > B) 不为真。
<检查左操作数的值是否小于右操作数的值,如果是则条件为真。(A < B) 为真。
>=检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。(A >= B) 不为真。
<=检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。(A <= B) 为真。

实例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int a = 10;
	int b = 20;

	if (a == b){
		printf("Line 1 - a 等于 b\n");
	}
	else{
		printf("Line 1 - a 不等于 b\n");
	}
	if (a < b){
		printf("Line 2 - a 小于 b\n");
	}
	else{
		printf("Line 2 - a 不小于 b\n");
	}
	if (a > b){
		printf("Line 3 - a 大于 b\n");
	}
	else{
		printf("Line 3 - a 不大于 b\n");
	}

	/* 改变 a 和 b 的值 */
	a = a + b;
	b = a - b;
	a = a - b;

	if (b <= a){
		printf("Line 4 - b 小于或等于 a\n");
	}
	if (a >= b){
		printf("Line 5 - a 大于或等于 b\n");
	}

	system("pause");
	return 0;
}

程序生成图:
在这里插入图片描述

逻辑运算符

下表为 C 语言支持的所有关系逻辑运算符。假设变量 A 的值为 1,变量 B 的值为 0,则:

运算符描述实例
&&称为逻辑与运算符。如果两个操作数都非零,则条件为真。(A && B) 为假。
||称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。(A || B) 为真。
!称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。!(A && B) 为真; !(A || B) 为假

||前者为true不执行后者,&&前者为false不执行后者

实例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int a = 1;
	int b = 0;

	if (a && b){
		printf("Line 1 - 条件为真\n");
	}
	else{
		printf("Line 1 - 条件不为真\n");
	}
	if (a || b){
		printf("Line 2 - 条件为真\n");
	}
	else{
		printf("Line 2 - 条件不为真\n");
	}
	if (!(a && b)){
		printf("Line 3 - 条件为真\n");
	}
	else{
		printf("Line 3 - 条件不为真\n");
	}
	if (!(a || b)){
		printf("Line 3 - 条件为真\n");
	}
	else{
		printf("Line 3 - 条件不为真\n");
	}

	system("pause");
	return 0;
}

程序生成图:
在这里插入图片描述

赋值运算符

下表列出 C 语言支持的赋值运算符:

运算符描述实例
=简单的赋值运算符,把右边操作数的值赋给左边操作数C = A + B 将把 A + B 的值赋给 C
+=加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数C += A 相当于 C = C + A
-=减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数C -= A 相当于 C = C - A
*=乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数C *= A 相当于 C = C * A
/=除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数C /= A 相当于 C = C / A
%=求模且赋值运算符,求两个操作数的模赋值给左边操作数C %= A 相当于 C = C % A
<<=左移且赋值运算符C <<= 2 等同于 C = C << 2
>>=右移且赋值运算符C >>= 2 等同于 C = C >> 2
&=按位与且赋值运算符C &= 2 等同于 C = C & 2
^=按位异或且赋值运算符C ^= 2 等同于 C = C ^ 2
|=按位或且赋值运算符C |= 2 等同于 C = C | 2

实例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int a = 10;
	int c = 0;

	c = a;
	printf("Line 1 - =  运算符实例,c 的值 = %d\n", c);
	c += a;
	printf("Line 2 - += 运算符实例,c 的值 = %d\n", c);
	c -= a;
	printf("Line 3 - -= 运算符实例,c 的值 = %d\n", c);
	c *= a;
	printf("Line 4 - *= 运算符实例,c 的值 = %d\n", c);
	c /= a;
	printf("Line 5 - /= 运算符实例,c 的值 = %d\n", c);

	c = 201;
	c %= a;
	printf("Line 6 - %= 运算符实例,c 的值 = %d\n", c);
	c <<= 2;
	printf("Line 7 - <<= 运算符实例,c 的值 = %d\n", c);
	c >>= 2;
	printf("Line 8 - >>= 运算符实例,c 的值 = %d\n", c);
	c &= 2;
	printf("Line 9 - &= 运算符实例,c 的值 = %d\n", c);
	c ^= 2;
	printf("Line 10 - ^= 运算符实例,c 的值 = %d\n", c);
	c |= 2;
	printf("Line 11 - |= 运算符实例,c 的值 = %d\n", c);

	system("pause");
	return 0;
}

程序生成图:
在这里插入图片描述

其他运算符 ↦ sizeof & 三目

下表列出了 C 语言支持的其他一些重要的运算符,包括 sizeof 和 ? :。

运算符描述实例
sizeof()返回变量的大小。sizeof(a) 将返回 4,其中 a 是整数。
&返回变量的地址。&a; 将给出变量的实际地址。
*间接访问操作符*a; 将指向一个变量。
? :条件表达式如果条件为真 ? 则值为 X : 否则值为 Y
[]下标引用操作符一个数组名 + 一个索引值 arr[9] = 10
()函数调用操作符 接受一个或者多个操作数第一个操作数是函数名,剩余的操作数就是传递给函数的参数。rexp[rexp]
.访问结构成员lexp.member_name
->访问结构指针成员rexp->member_name
逗号rexp,rexp
#include <stdio.h>
#include <stdlib.h>

void test1()
{
	printf("hehe\n");
}

int main()
{
	int a = 4;
	short b;
	double c;
	int* ptr;

	/* sizeof 运算符实例 */
	printf("Line 1 - 变量 a 的大小 = %lu\n", sizeof(a));
	printf("Line 2 - 变量 b 的大小 = %lu\n", sizeof(b));
	printf("Line 3 - 变量 c 的大小 = %lu\n", sizeof(c));

	/* & 和 * 运算符实例 */
	ptr = &a;    /* 'ptr' 现在包含 'a' 的地址 */
	printf("a 的值是 %d\n", a);
	printf("*ptr 是 %d\n", *ptr);

	/* 三目运算符实例 */
	a = 10;
	b = (a == 1) ? 20 : 30;
	printf("b 的值是 %d\n", b);

	b = (a == 10) ? 20 : 30;
	printf("b 的值是 %d\n", b);

	int arr[10];//创建数组
	arr[9] = 10;//实用下标引用操作符。
	printf("arr[9] 的值是 %d\n", arr[9]);

	test1();

	system("pause");
	return 0;
}

程序生成图:
在这里插入图片描述

逗号表达式的说明:

表达式1,表达式2,表达式3,… ,表达式n
逗号表达式的要领:

  1. 逗号表达式的运算过程为:从左往右逐个计算表达式。
  2. 逗号表达式作为一个整体,它的值为最后一个表达式(也即表达式n)的值。
  3. 逗号运算符的优先级别在所有运算符中最低。

实例:
(3+5,6+8) 称为逗号表达式,其求解过程先表达式1,后表达式2,整个表达式值是表达式2的值 14
a=(a=35,a4)的值是60,其中(a=35,a4)的值是60, a的值在逗号表达式里一直是15,最后被逗号表达式赋值为60,a的值最终为 60

C 中的运算符优先级

运算符的优先级确定表达式中项的组合。这会影响到一个表达式如何计算。

指针 > 单目运算符 > 双目运算符(算术运算符 > 移位运算符 > 位运算符 > 逻辑运算符)> 三目运算符 > 赋值运算符 > 逗号运算符

下表将按运算符优先级从高到低列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面。在表达式中,较高优先级的运算符会优先被计算。

类别运算符结合性
后缀() [] -> . ++ --从左到右
一元+ - ! ~ ++ -- (type)* & sizeof从右到左
乘除* / %从左到右
加减+ -从左到右
移位<< >>从左到右
关系< <= > >=从左到右
相等== !=从左到右
位与 AND&从左到右
位异或 XOR^从左到右
位或 OR|从左到右
逻辑与 AND&&从左到右
逻辑或 OR||从左到右
条件?:从右到左
赋值= += -= *= /= %=>>= <<= &= ^= |=从右到左
逗号,从左到右

习题

  1. 输入参数为197时,函数返回多少?
int Function(unsigned int n) {
	n = (n & 0x55555555) + ((n >> 1) & 0x55555555); 
	n = (n & 0x33333333) + ((n >> 2) & 0x33333333); 
	n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
	n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff);
	n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);
	return n; 
}

A. 2
B. 3
C. 4
D. 5

正确答案:

C

答案解析:

0x55555555:01010101 01010101 01010101 01010101
0x33333333:00110011 00110011 00110011 00110011
0x0f0f0f0f: 00001111 00001111 00001111 00001111
0x00ff00ff: 00000000 11111111 00000000 11111111
0x0000ffff: 00000000 00000000 11111111 11111111

这里运用了分治法计算二进制数中1的个数 ,这个算法叫做平行算法。

(n & 0x55555555) + ((n >> 1) & 0x55555555) 计算每对相邻的2位中有几个1
(n & 0x33333333) + ((n >> 2) & 0x33333333) 计算每相邻的4位中有几个1
接下来8位,16位,32位,对于32位的机器,5条位运算语句就够了。

其实很简单,先将n写成二进制形式,然后相邻位相加,重复这个过程,直到只剩下一位。

举例:

197: 11000101

  1. 1000 0101
  2. 0010 0010
  3. 0000 0100 (所以答案是4)

  1. In the main() function, after ModifyString(text) is called, what’s the value of ‘text’?
int FindSubString( char* pch )
{
    int   count  = 0;
    char  * p1   = pch;
    while ( *p1 != '\0' )
    {   
        if ( *p1 == p1[1] - 1 )
        {
            p1++;
            count++;
        }else  {
            break;
        }
    }
    int count2 = count;
    while ( *p1 != '\0' )
    {
        if ( *p1 == p1[1] + 1 )
        {
            p1++;
            count2--;
        }else  {
            break;
        }
    }
    if ( count2 == 0 )
        return(count);
    return(0);
}
void ModifyString( char* pText )
{
    char  * p1   = pText;
    char  * p2   = p1;
    while ( *p1 != '\0' )
    {
        int count = FindSubString( p1 );
        if ( count > 0 )
        {
            *p2++ = *p1;
            sprintf( p2, "%i", count );
            while ( *p2 != '\0' )
            {
                p2++;
            }
            p1 += count + count + 1;
        }else  {
            *p2++ = *p1++;
        }
    }
}
void main( void )
{
    char text[32] = "XYBCDCBABABA";
    ModifyString( text );
    printf( text );
}  

A. XYBCDCBABABA
B. XYBCBCDA1BAA
C. XYBCDCBA1BAA
D. XYBCDDBA1BAB

正确答案

C

答案解析

*p1++ = *p2++

c语言从右往左压栈,

  1. 先计算p2++,产生对p2的一份拷贝,

  2. ++操作符增加p2的值(将指针p2向后移动1个自身长度的偏移量);

  3. 再计算*p2:对p2的拷贝上执行解引用;

  4. 再计算p1++,产生对p1的一份拷贝,

  5. ++操作符增加p1的值(将指针p1向后移动1个自身长度的偏移量);

  6. 再计算*p1:对p1的拷贝上执行解引用;

  7. 最后将第3步所得结果赋到第6步中的内存,即*p1=*p2;

简单理解: *p1=*p2; p1+1,p2+1;

FindSubString() 函数就是要找到一个先递增再递减且递增和递减的数量相等的回文序列,例如: ABCDCBA ,先是 后一项 = 前一项 ASCII 码 +1 , 后是 后一项 = 前一项 ASCII 码 -1 ,才能返回回文子串的长度,否则返回 0 。

ModifyString() 函数不断寻找上述类型的子串,如果不满足条件,就

*p2++ = *p1++;

当遇到 ABABA 中前一个 ABA 的时候,满足回文子串要求,此时 p1 指向 A BABA , p2 指向 ABABA ; sprintf 重定向修改 ABABA , B 变为 1 ,且跟随一个 ‘\0’ (该函数自动产生的) , 此时,字符串变为 A1‘\0’BA 。

经过 while ( *p2 != ‘\0’ ) 循环之后, p2 指向 A1‘\0’BA , p1 += count + count + 1 之后, p1 指向 A1‘\0’BA 。此时字符串已经被改动,之前的 ABABA 已经不存在,变为 A1‘\0’BA 。

再次进入 while ( *p1 != ‘\0’ ) 循环之后,只能执行 else 部分的命令, p1 指向 p2 指向的元素的后一个,不断将 p1 指向的元素传给 p2 所指向的位置,将原数据覆盖。所以, A1‘\0’BA ,依次变为 A1BBA 、 A1BAA 。即最终结果为 XYBCDCBA1BAA 。

int i=10,j=10,k=3;k*=i+j

k最后的值是?

A. 60
B. 40
C. 50
D. 23

正确答案:A

答案解析:

等价于下面的式子

int i=10,j=10,k=3;
k=k*(i+j)
  1. 若有定义语句:int year=2009, int* p=&year ;以下不能使变量year中的值增至2010的语句是(  )。

A. (*p)++;
B. *p++;
C. ++(*p);
D. *p+=1;

正确答案:

B

答案解析:

指针变量存储的是其指向的变量的地址,*p表示的是p指向的变量的值,自加运算符(++)的优先级要高于取值运算符( * )的优先级,*p++表示的是先将指针p指向下一个地址然后再取该地址的值,所以得到的结果并不是把year的值增至2010

  1. 下面程序的输出结果是__________。
#include <iostream> 
#define SQR(A) A*A 
int main() {
	int x=6,y=3,z=2; 
	x/=SQR(y+z)/SQR(y+z); 
	cout< < x< < endl;
}

A、0
B、6
C、5
D、1

正确答案: A

答案解析:

x/=SQR(y+z)/SQR(y+z); 等效于x= x / (SQR(y+z)/SQR(y+z));
所以 x = 6 / (3 + 2 * 3 + 2 / 3 + 2 * 3 + 2)


如有不同见解,欢迎留言讨论~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值