IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言操作符(运算符)的详细解析 -> 正文阅读

[C++知识库]C语言操作符(运算符)的详细解析

目录

前言

一、操作符分类

二、算术操作符

三、移位操作符

1.左移操作符

2.右移操作符

?四、位操作符

1.&按位与

2. | 按位或?

3.^按位异或

?4.位操作符的练习题

五、赋值操作符

1.复合赋值符

六、单目操作符

1.单目操作符介绍

2.!逻辑反操作符

3.sizeof操作符

3.1 sizeof操作符的注意事项

?3.2 sizeof操作符和数组

4.~按位取反

5.自加自减举例

6.&取地址操作符和*解引用操作符

七、关系操作符

八、逻辑操作符

九、条件操作符

十、逗号表达式

十一、下标引用、函数调用和结构成员

1.[ ]下标引用操作符

2.( )函数调用操作符

3.访问一个结构体成员

十二、表达式求值

1.隐式类型转换

2.算术转换

3.操作符的属性

4.一些问题表达式


前言

本文章详细地和各位小伙伴展示了我对C语言操作符的理解,欢迎大家一起讨论!

一、操作符分类

  • 算术操作符
  • 移位操作符
  • 位操作符
  • 赋值操作符
  • 单目操作符
  • 关系操作符
  • 逻辑操作符
  • 条件操作符
  • 逗号表达式
  • 下标引用、函数调用和结构成员调用

二、算术操作符

+ ????????- ????????*? ? ? ? /? ? ? ? %

1.除了%操作符之外,其他的几个操作符可以作用于整数和浮点数。

2.对于/操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行就是浮点数除法。?

#include<stdio.h>
void main() {
	//下面两个表达式都是以整数为操作数
	int a = 10 / 3;
	double b = 10 / 3;
	//一个操作数为小数形式
	double c = 10.0 / 3;
	//两个操作数都为小数形式
	double d = 10.0 / 3.0;
	printf("a=%d,b=%f,c=%f,d=%f", a, b, c, d);
}

?注意:不管最终接收的数据是int型,还是float型,计算结果的值都是由操作数本身的数据类型来控制的。

3.%操作符的两个操作数必须为整数。返回的是整数之后的余数

int main()
{
	int a = 7 % 3;//余1
	printf("%d\n", a);
	return 0;
}

三、移位操作符

<<? 左移操作符

>>? 右移操作符

注意:移位操作符的操作数只能是整数(因为是转化成二进制进行移位操作)

1.左移操作符

#include<stdio.h>
void main() {
	int a = 2;
	int b = a << 1;
	printf("a=%d,b=%d", a, b);
}

?想要了解此操作符的意义,那我们就必须将十进制2转化为对应的int的二进制数

①十进制数2对应的二进制数:00000000000000000000000000000010(32位)

②a<<1表示将此二进制数的所有位都向移动一位,左边丢弃,右边补0。也就得到

00000000000000000000000000000100(32位)也就是提升了此数的一个权位,来到了2^2部分,所以左移操作符相当于该数乘以2。

注意:虽然说a是进行了左移,但不意味着a的值有变化,只是将a进行左移后的值赋值给b,他本身没有发生变化。

③还有如下代码证明

2.右移操作符

移位规则:首先右移运算分为两种

①逻辑移位:左边用0填充,右边丢弃。

②算术移位:左边用原该值的符号位填充,右边丢弃。

例子1:以正整数为例

?首先由于这个4是正整数,所以无论是进行逻辑移位,还是算术移位,都是在往左边补0

①十进制正整数4的二进制数为:00000000000000000000000000000100(32位)

②右移之后就变成了:00000000000000000000000000000010(32位)

③从上面的例子不难总结右移操作符就是在原来的数除以2。

例子2:以负整数为例

?首先我们得知道负数是以补码的形式进行存储的,那么怎么将一个数从原码转化成补码呢?转换顺序为:原码->反码->补码,如下所示:

(1)原码:直接根据数值写出的二进制序列就是原码。

(2)反码:原码的符号位不变,其他位按位取反就是反码。

(3)补码:反码+1,就是补码。

注意:正整数的补码就是他的原码

我们知道了原码怎么转变为补码后,就来推推这个列子

①十进制数的-1的二进制原码是:10000000000000000000000000000001(第一位的数值表示正负号,0表示正,1表示负)。

②对应的反码:11111111111111111111111111111110。

③对应的补码:11111111111111111111111111111111。

④对11111111111111111111111111111111进行右移一位(算术右移)得到的的二进制数为:11111111111111111111111111111111,那么该值也是为-1的补码。

注意:内存里是以补码形式存储,但是结果是以原码形式展现的。

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

?四、位操作符

位操作符有:

&? 按位与

|? ?按位或

^? 按位异或

注意:他们的操作数都必须是整数

1.&按位与

按位与规则:只有两个对应位置的数都为1,结果才为1;如果有是0对1或者0对0,结果都为0,包括符号位也要变化。(有点像逻辑操作符的&&)

例子1:以正整数和正整数为例

①我们得知道?3,5所对应的补码,如上所示,

②然后按照对应的规则进行按位与&

?例子2:以负整数和正整数为例

#include<stdio.h>
void main() {
	int a = -2;
	int b = 3;
	int c = a & b;
//-2原码:10000000000000000000000000000010
//-2反码:11111111111111111111111111111101
//-2补码     11111111111111111111111111111110
//3的补码  00000000000000000000000000000011
//-2 & 3 为:00000000000000000000000000000010(结果为2)
	printf("c=%d", c);
}

①还是一样得知道-2的补码,3的补码。

②然后按照对应的规则进行按位与&。

例子3:以负整数和负整数为例

#include<stdio.h>
void main() {
	int a = -1;
	int b = -2;
	int c = a & b;
	//-1的补码:11111111111111111111111111111111
	//-2的补码:11111111111111111111111111111110
	//-1&-2:11111111111111111111111111111110(记为x)
	//x的反码:11111111111111111111111111111101
	//x的原码:10000000000000000000000000000010(为-2)
	printf("c=%d", c);
}

①首先得知道-2,-1的补码

②?按照对应的规则进行按位与&,结果为-2

2. | 按位或?

按位或规则:只要对应的位置有一个1,此位置生成的数也是1,如0对1或者1对1,结果都为1;如果对应的是0对0,那么结果就是0,包括符号位也要变化。(有点像逻辑操作符的||)

例子1:以正整数和正整数为例

①知道5,3的补码

②按照按位或的规则进行运算,得出结果为7?

例子2:以负整数和正整数为例

#include<stdio.h>
void main() {
	int a = -2;
	int b = 3;
	int c = a | b;
//-2原码:10000000000000000000000000000010
//-2反码:11111111111111111111111111111101
//-2补码     11111111111111111111111111111110
//3的补码  00000000000000000000000000000011
//-2 | 3 为:11111111111111111111111111111111(记为x)
//x的原码为:
//11111111111111111111111111111110(反码)
//10000000000000000000000000000001(原码)结果为-1
	printf("c=%d", c);
}

①首先得知道-2的补码和3的补码

②然后按照按位或的规则进行运算,得出结果为-1

例子3:以负整数和负整数例子

#include<stdio.h>
void main() {
	int a = -1;
	int b = -2;
	int c = a | b;
	//-1的补码:11111111111111111111111111111111
	//-2的补码:11111111111111111111111111111110
	//-1|-2:11111111111111111111111111111111(记为x)
	//x的反码:11111111111111111111111111111110
	//x的原码:10000000000000000000000000000001(为-1)
	printf("c=%d", c);
}

①的指导-1和-2的补码

②然后按照按位或的规则进行运算,得出结果为-1

3.^按位异或

按位异或规则:按(2进制)位异或,对应的二进制位进行异或,相同为0,相异为1,包括符号位也要变化。

例子1:以正整数和正整数为例

例子2:以正整数和负整数为例

?例子3:以负整数和负整数为例?

?4.位操作符的练习题

例1:不能创建临时变量(第三个变量),实现两个数的交换

#include<stdio.h>
void main() {
	int a = 5;
	int b = 3;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("a=%d,b=%d", a, b);
}

?其实我们还可以从另一个角度去理解:首先我们的指导0^任何数的值都为该数的值;一个数去^自己本身的值为0,例子如下:

?那么a ^ b ^ b可以理解为:b ^ b == 0,a ^ 0 = a,那么b = a(此时b的值就为a的值了,完成了交换)

解决两个数值交换的方式还有很多,如下:

int main()
{
    int a = 3;
   	int b = 5;
	//交换
    printf("a = %d b = %d\n", a, b);
	//数值太大会溢出
	a = a + b;
	b = a - b;
	a = a - b;
	printf("a = %d b = %d\n", a, b);

	int c = 0;//空瓶
	printf("a = %d b = %d\n", a, b);
	c = a;
	a = b;
	b = c;
	printf("a = %d b = %d\n", a, b);

	return 0;
}

例2:编写代码实现求一个整数存储在内存中的二进制中1的个数

方法一:运用了位操作符和移位操作符

思路如下图:?

?代码如下:

void main() {
	int a = 5;
	int count = 0;
	int i;
	for (i = 0;i < 32;i++) {
		if (a & (1 << i)) {
			count++;
		}
	}
	printf("%d", count);//结果为2
}

方法二:利用十进制转二进制的思路,用余数来记录1的个数

void main() {
	int a = 5;
	int count = 0;
	int i;
	while (a) {
		if (a % 2 == 1) {
			count++;
		}
		a /= 2;
	}
	printf("%d", count);//结果为2
}

方法三:采用相邻的两个数据进行按位与运算

#include<stdio.h>
void main() {
	int a = 5;
	int count = 0;
	while (a) {
		a = a & (a - 1);
		count++;
	}
	printf("%d", count);//结果为2
}

五、赋值操作符

赋值操作符是一个很好用的操作符,他可以让你重新对一个变量进行赋值。(赋值操作符为“=”)

void main() {
	int weight = 120;//体重
	weight = 100;//使用赋值操作符重新赋值
	double salary = 10000.0;//money
	salary = 200000.0;//使用赋值操作符重新赋值
}
int a = 10;
int x = 0;
int y = 20;
//连续赋值,一般不会这么用,影响了可读性
a = x = y + 1;

1.复合赋值符

  • +=:a += b,a与b的和赋值给a
  • -=:a -= b,a与b的差赋值给a
  • *=:a *= b,a与b的积赋值给a
  • /=:a /= b,a与b的商赋值给a
  • %=:a %= b,a与b的余数赋值给a
  • >>=:a >>= b,a右移b个位后的值赋值给a
  • <<=:a <<= b,a左移b个位后的值赋值给a
  • &=:a &= b,a与b按位与后的值赋值给a
  • |=:a |= b,a与b按位或后的值赋值给a
  • ^=:a ^= b,a与b按位异或后的值赋值给a

这些操作符都可以达到复合的效果!

int a = 10;
a = a + 10;
a += 10;//复合赋值
//其他运算符一样的道理,这样写更加简洁

六、单目操作符

1.单目操作符介绍

  • ? ? ? ? 逻辑反操作
  • -? ? ? ? ? 负值
  • +? ? ? ? ?正值
  • &? ? ? ? ?取地址
  • sizeof? 操作数的类型长度(以字节为单位)
  • ~? ? ? ? ?对一个数的二进制按位取反
  • --? ? ? ? ?前置、后置--
  • ++? ? ? ?前置、后置++
  • *? ? ? ? ? 间接访问操作符(解引用操作符)
  • (类型)? 气质类型转换

2.!逻辑反操作符

int main()
{
	int flag = 0;
	printf("%d\n", !flag);
	//flag为真,打印hehe
	if (flag)
	{
		printf("hehe\n");
	}
	//flag为假,打印haha
	if (!flag)
	{
		printf("haha\n");
	}
	return 0;
}

3.sizeof操作符

3.1 sizeof操作符的注意事项

sizeof可以用来求变量(类型)所占的空间大小

#include<stdio.h>
void main() {
	short s = 5;
	int a = 10;
	printf("%d\n", sizeof(s = a + 2));
	printf("%d\n", s);
	/*
		sizeof的执行是发生在编译时期,而a + 2的运算是发生在运行时期。
		所以这里的sizeof是发生在a + 2之前的,所以不会有类型提升的说法,
		同时不管你类型是否提升,最终都是放到short型里,所以都是short型。
        sizeof在编译期就结束了,sizeof只想知道你的类型是什么,它不管你
        是否有进行了运算,就直接结束了。
	*/
	int arr[10] = { 0 };
	printf("%d\n", sizeof(arr));//单位是字节
	printf("%d\n", sizeof(int[10]));//40 - int [10]是arr数组的类型
	printf("%d\n", sizeof(a));//计算a所占空间的大小,单位是字节
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof a);//说明了sizeof是一个操作符而不是一个函数
}

编译结果如下:

?3.2 sizeof操作符和数组

#include <stdio.h>
void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//(2)
}
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//(4)
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof arr);//(1)
	printf("%d\n", sizeof(ch));//(3)
	test1(arr);
	test2(ch);
	return 0;
}

问:(1)(2)(3)(4)分别是输出什么?答案如下:

位置(1):sizeof arr就是输出int arr[10]的数组长度,也就是40个字节。

位置(2):sizeof(ch)就是输出char ch[10]的数组长度,也就是10个字节。

位置(3):首先我们得知道,传到函数test1()里面的实参虽然是arr,但是它的本质其实是一个指针,也就是int* arr,所以test1()函数里面的sizeof(arr)就是输出int的大小,也就是4个字节。

位置(4):同理于位置(3),也就是输出指针char* ch的大小,也就是4个字节。

注意:无论是什么类型的,他的地址长度是看操作位数决定的32位就是4个字节,64位就是8个字节。

4.~按位取反

int main()
{
	int a = -1;
	//10000000000000000000000000000001 - 原码
	//11111111111111111111111111111110 - 反码
	//11111111111111111111111111111111 - 补码
	//~ 按位取反
	//11111111111111111111111111111111
	//00000000000000000000000000000000
	//
	int b = ~a;
	printf("%d\n", a);
	printf("%d\n", b);

	return 0;
}

按位取反操作符是连同符号位也会进行取反。

5.自加自减举例

#include <stdio.h>
void main() {
	int a = 10;
	printf("%d\n", a--);//10
	printf("%d\n", a);//9
}

注意:后置++或者后置--,都是等到整条语句结束的时候才进行自加或自减。

6.&取地址操作符和*解引用操作符

int main()
{
	int a = 10;
	printf("%p\n", &a);//& - 取地址操作符
	int * pa = &a;//pa是用来存放地址的 - pa就是一个指针变量
	*pa = 20;//* - 解引用操作符 - 间接访问操作符
	printf("%d\n", a);//20

	return 0;
}

七、关系操作符

  • >
  • >=
  • <
  • <=
  • !=? ? ? ? 用于测试“不相等”
  • ==? ? ? ?用于测试“相等”

这些关系运算符比较简单,没什么可细述,注意别在编程中把==和=混淆就行。

八、逻辑操作符

逻辑操作符有哪些:

  • &&? ? ? ? 逻辑与
  • ||? ? ? ? ? ?逻辑或

注意:区分逻辑与和按位与;区分逻辑或和按位或。

1 & 2 -----> 0
1 && 2 ----> 1
1 | 2 -----> 3
1 || 2 ----> 1
例题:360笔试题
#include <stdio.h>
int main()
{
 ? ?int i = 0,a=0,b=2,c =3,d=4;
 ? ?i = a++ && ++b && d++;
 ? ?//i = a++||++b||d++;
 ? ?printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
 ? ?return 0; }
//程序输出的结果是什么?

逻辑与和逻辑或都有“短路”的效果。

当逻辑与的左边第一个为假,那么右边就不会执行了,直接结束

当逻辑或的左边第一个为真,那么右边就不会执行了,直接结束

按照上面的思路:结果为

a = 1

b = 2

c = 3

d = 4

九、条件操作符

条件操作符也称为三目运算符

结构:exp1 ? exp2 : exp3

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

	if (a > 5)
		b = 1;
	else
		b = -1;

	//三目操作符
	b = (a > 5 ? 1 : -1);
	return 0;
}

十、逗号表达式

结构:exp1, exp2, exp3, …expN

逗号表达式,就是用逗号隔开的多个表达式。

逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果

int main()
{
	int a = 3;
	int b = 5;
	int c = 0;
	//逗号表达式 - 要从做向右依次计算,但是整个表达式的结果是最后一个表达式的结果

	int d = (c = 1, a = c + 3, b = a - 4, c += b);

	        //c=10  a=8         b=4    
	printf("%d\n", d);//结果为10,也就是c的值

	return 0;
}

十一、下标引用、函数调用和结构成员

1.[ ]下标引用操作符

操作数:一个数组名+一个索引值

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//              0 1     4
	printf("%d\n", arr[4]);//[] - 就是下标引用操作符
	//[] 的操作数是2个:arr , 4
	return 0;
}

2.( )函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的实参。

//函数的定义
int Add(int x, int y)
{
	return x + y;
}
void test()
{}

int main()
{
	int a = 10;
	int b = 20;
	//函数调用
	int ret = Add(a, b);//() - 函数调用操作符
	test();

	return 0;
}

3.访问一个结构体成员

.? ? ? ? ?结构体.成员名

->? ? ? ?结构体指针->成员名

struct Book
{
	//结构体的成员(变量)
	char name[20];
	char id[20];
	int price;
};

int main()
{
	//int num = 10;
	//结构体变量名.成员名
	struct Book b = {"C语言", "C20210509", 55};
	struct Book * pb = &b;

	//结构体指针->成员名
	printf("书名:%s\n", pb->name);
	printf("书号:%s\n", pb->id);
	printf("定价:%d\n", pb->price);
    //利用*解引用知道结构体变量b,在用点操作符去访问结构体成员
	printf("书名:%s\n", (*pb).name);
	printf("书号:%s\n", (*pb).id);
	printf("定价:%d\n", (*pb).price);
    //直接用结构体变量b加点操作符去访问结构体成员
	printf("书名:%s\n", b.name);
	printf("书号:%s\n", b.id);
	printf("定价:%d\n", b.price);

	return 0;
}

十二、表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。

同样有些表达式操作符在求值的过程中可能需要转换为其他类型。

1.隐式类型转换

c的整型算术运算总是至少以缺省整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

整型提升的意义:

表达式的整型运算要自CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

通用CPU(general-purpose CPU)是难以直接实现两个8比特位直接相加运算(虽然机器指令中可能有这种字节的相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行。

例子1:

int main()
{
	char a = 3;
	//00000000000000000000000000000011
	//00000011 - a
	char b = 127;
	//00000000000000000000000001111111
	//01111111 - b

	char c = a + b;
	//00000000000000000000000000000011
	//00000000000000000000000001111111
	//00000000000000000000000010000010
	
	//10000010 - c,此数就是将原来的值截断
    
    //%d是按int类型输出,所以这里又是类型提升
	printf("%d\n", c); //-126
    //11111111111111111111111110000010 - 补码
	//11111111111111111111111110000001 - 反码
	//10000000000000000000000001111110 - 原码
	//-126
	//发现a和b都是char类型的,都没有达到一个int的大小
	//这里就会发生整形提升
	return 0;
}

这里因为%d要按照int类型输出,所以就发生了隐式类型提升,符号位是什么就往左补符号位相对应的数。也就是整型提升是按照变量的数据类型的符号位来提升。

例子2:

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

例子3:

//实例1
int main()
{
 char a = 0xb6;
 short b = 0xb600;
 int c = 0xb6000000;
 if(a==0xb6)
 printf("a");
 if(b==0xb600)
 printf("b");
 if(c==0xb6000000)
 printf("c");
 return 0; }

例子3中的a,b要进行整型提升,但是c不需要整型提升

a,b整型提升之后,变成了负数,所以表达式a==0xb6,b==0x600的结果为假,但是c不发生整型提升,则表达式c==0x6000000的结果为正

结果输出c

例子4:

(一)

?(二)

首先我们得知道一个运算表达式他是具有两个属性,分别是值属性和类型属性。

值属性:是通过运算后得到的值,可以理解为是在运行时期发生。

类型属性:是通过推断后得到的,可以理解为在编译时期就发生了。

(一)c本身没进行运算,所以结果为1;第2、第3和第4条输出语句中都有运算表达式,所以发生了类型属性推断,也就是隐式类型转换。(在gcc环境中,第四条语句的结果为4,vs的编译环境不太对!所以为1)

(二)首先我们知道了sizeof的类型判断和运算表达式的类型推断都是发生在编译期,所以我们就可以分为两部分来看

①先看s = a + 3,不管它在运算时怎么类型转换,最终都是short型,那么此运算表达式的类型推断为short型。

②sizeof得知了s = a + 3的类型推断为short型,那么它的值也就是2。

2.算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系成为寻常算术转换。

  1. long double
  2. double
  3. float
  4. unsigned long int
  5. long int
  6. unsigned int
  7. int?

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。但是算术转换要合理,要不然会有一些潜在的问题。

3.操作符的属性

复杂表达式的求值有三个影响的因素

(1)操作符的优先级

(2)操作符的结合性

(3)是否控制求值顺序

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

操作符的优先级如下:

操作符的优先级
操作符描述结合性是否控制求值顺序
()聚组N/A
()函数调用L-R
[ ]下标引用L-R
.访问结构体成员L-R
->访问结构指针成员L-R
++后缀自增L-R
--后缀自减L-R
逻辑反L-R
~按位取反R-L
+单目,表示正值R-L
-单目,表示负值R-L
++前缀自增R-L
--前缀自减R-L
*间接访问(解引用)R-L
&取地址R-L
sizeof取其长度,以字节表示R-L
(类型)类型转换R-L
*乘法L-R
/除法L-R
%整数取余L-R
+加法L-R
-减法L-R
<<左移位

L-R

>>右移位L-R
>大于L-R
>=大于等于L-R
<小于L-R
<=小于等于L-R
==等于L-R
!=不等于

L-R

&按位与L-R
^按位异或L-R
|按位或L-R
&&逻辑与L-R
||逻辑或L-R
?:条件操作符

N/A

=赋值R-L
+=加后赋值R-L
-=减后赋值R-L
*=乘后赋值R-L
/=除后赋值R-L
%=取余后赋值R-L
<<=左移后赋值R-L
>>=右移后赋值R-L
&=按位与后赋值R-L
^=按位异或后赋值R-L
|=按位或后赋值R-L
逗号L-R

总结:

(1)单(目)>算(术)>关(系)>逻(辑)>赋(值)

(2)单目运算符和赋值运算符:自右至左结合

(3)其它的:自左到右

4.一些问题表达式

例子1:

//表达式的求值部分由操作符的优先级决定。
//表达式1 
a*b + c*d + e*f

代码在计算的时候,由于*比+优先级高,只能保证*的计算比+早,但是优先级并不能决定第三个*比第一个+早执行

所以表达式在计算机顺序就可能是:

a * b
c * d
a * b + c * d
e * f
a * b + c * d + e * f
或者:
a * b
c * d
e * f
a * b + c * d
a * b + c * d + e * f

列子2:

//表达式2 
c + --c;

同上的道理,操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得知,+操作符的做操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是由歧义的。

例子3:

//代码3-非法表达式
int main()
{
    int i = 10;
    i = i-- - --i * ( i = -3 ) * i++ + ++i;
    printf("i = %d\n", i);
    return 0; 
}

例子3在不同的编译器中测试结果:非法表达式程序的结果

编译器
-128Tandy 6000 Xenix3.2
-95Think C 5.02(Macintosh)
-86IBM PowerPC AIX 3.2.5
-85Sun Sparc cc(K&C编译器)
-63gcc
4Sun Sparc acc(K&C编译器)
21Turbo C/C++ 4.5
42Microsoft C 5.1

例子4:

//代码4
int fun()
{
 ? ? static int count = 1;
 ? ? return ++count; 
}
int main()
{
 ? ? int answer;
 ? ? answer = fun() - fun() * fun();
 ? ? printf( "%d\n", answer);//输出多少?
 ? ? return 0; 
}

有问题!

虽然在大多数的编译器上求得结果都是相同的。

但是上述代码answer = fun() - fun() * fun();中我们只能通过操作符的优先级得知:先算乘法,再算减法。?

其实也可以换个思路来理解:函数调用操作符()的优先级高于算术操作符(也叫算术运算符),并且他的操作顺序是从左往右的,等函数调用操作符运算完成之后,才开始算术运算符的操作。显然这并不是我们想要的执行顺序,我们的思路和计算机的运算规律产生了歧义。

例子5:

//代码5
#include <stdio.h>
int main()
{
    int i = 1;
    int ret = (++i) + (++i) + (++i);
    printf("%d\n", ret);
    printf("%d\n", i);
    return 0; 
}
//尝试在linux 环境gcc编译器,VS2019环境下都执行,看结果。

VS2013的环境下:

有的小伙伴就感觉奇怪了,为什么不是2+3+4=9呢?而是4+4+4=12呢?

(1)这就需要我们去了解一下汇编语言,首先得知道计算机有许多寄存器(读取效率:寄存器>内存>磁盘)(寄存器有:eax、ebx、ecx、edx、ebp、esp等等)。

(2)用vs2019进行代码调试,在ret变量那里打开反汇编,就可以看到此例子第2张图片的界面,我们可以发现i的值是寄存到ecx寄存器中的,由寄存器去帮我们进行运算,最终得到一个值,也就是说他是执行了3次++i后才进行的算术运算,所以结果就为12。

(3)可以理解为变量i是从寄存器ecx中取值的步骤,而ecx只有一个且是统一执行到结束,里面存放的结果就是4,所以所有的i都为4。

?在调试的状态下,对变量ret右键,就可以看到有转到反汇编的指令,点击就行了。

呼!12340个字,终于写完了,重新对操作符进行学习,加深了我对C语言操作符的认识,感觉还挺有趣的!

点个赞呗!

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 00:51:43  更:2022-09-04 00:54:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 10:35:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码