C语言——操作符

2024-01-02 22:28:50

一、算数操作符

1、+(加操作符)

用于将两个数相加,例:3 + 3结果为6

2、-(减操作符)

用于将两个数相减,例:3 - 3结果为0

3、*(乘操作符)

用于将两个数相乘,例:3 * 3结果为9

4、/(除操作符)

用于将两个数相除,例:3 / 3结果为1

有两种模式,整数除法和浮点数除法:

整数除法,/的两个操作数都是整数,例:1 / 3结果为0,因为为整数除法,商0余1;

浮点数除法,/的两个操作数其中有一个是浮点数,例:1.0 / 4结果为0.25。

5、%(取余操作符)

%的两个操作数必须是整数,作用是在整数除法中获取余数,例:1 %?3的结果为1

#include <stdio.h>

int main()
{
	int a = 1, b = 2;
	printf("%d\n", a + b);
	printf("%d\n", a - b);
	printf("%d\n", a * b);
	printf("%d\n", a / b);
	printf("%d\n", a % b);
	return 0;
}

6、++(自增操作符)

将操作数加一,例a = 3,则a++为4

int a = 1;
a++;
printf("%d\n",a);

(1)后置++

先赋值,后运算;先操作,后计算。

	int a = 0;
	int b = a++;//后置++,先赋值后运算
				//相当于b = a;a = a + 1
	printf("a = %d\nb = %d\n", a, b);//最终a = 1,b = 0

(2)前置++

先运算,后赋值;先计算,后操作。

	int a = 0;
	int b = ++a;//前置++,先运算后赋值
				//相当于a = a + 1;b = a
	printf("a = %d\nb = %d\n", a, b);//最终a = 1,b = 1

7、--(自减操作符)

将操作数减一,例a = 3,则a--为2

int a = 1;
a--;
printf("%d\n",a);

(1)后置--

先赋值,后运算;先操作,后计算。

	int a = 0;
	int b = a--;//后置--,先赋值后运算
				//相当于b = a;a = a - 1
	printf("a = %d\nb = %d\n", a, b);//最终a = -1,b = 0

(2)前置--

先运算,后赋值;先计算,后操作。

	int a = 0;
	int b = --a;//前置--,先运算后赋值
				//相当于a = a - 1;b = a
	printf("a = %d\nb = %d\n", a, b);//最终a = -1,b = -1

自增和自减操作符分为前置和后置,详细请看我之前的文章《C语言——自增与自减-CSDN博客》。

二、关系操作符

1、==

检查两个操作数的值是否相等,如果相等则条件为真,否则为假。

(1?== 2) 为假。

2、!=

检查两个操作数的值是否相等,如果不相等则条件为真,否则为假。

(1 != 2) 为真。

3、>

检查左操作数的值是否大于右操作数的值,如果是则条件为真,否则为假。

(1 > 2)为假

4、<

检查左操作数的值是否小于右操作数的值,如果是则条件为真,否则为假。

(1 < 2)为真

5、>=

检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真,否则为假。

(1 >= 2)为假

6、<=

检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真,否则为假。

(1 <= 2)为真

#include <stdio.h>

int main()
{
	int a = 1, b = 2;
	if (a == b)
	{
		printf("a == b ture\n");
	}
	if (a != b)
	{
		printf("a != b ture\n");
	}
	if (a > b)
	{
		printf("a > b ture\n");
	}
	if (a < b)
	{
		printf("a < b ture\n");
	}
	if (a >= b)
	{
		printf("a >= b ture\n");
	}
	if (a <= b)
	{
		printf("a <= b ture\n");
	}
	return 0;
}

结果为:

三、逻辑操作符

1、&&

称为逻辑与运算符。如果两个操作数都非零,则条件为真。

(1 && 0) 为假。

一些性质:

(1)短路性质:当运算符&&前的表达式为假(即为0)时,整个逻辑表达式的结果已经确定为假,那么编译器不会再去计算&&后面的表达式。这被称为"短路",因为后面的表达式被"短路"掉了。这样不仅可以提高效率,有时也可以用来在&&后面的表达式中避免潜在的错误(比如除0错误)。

(2)结合性:&&具有左结合性,即多个&&运算符在表达式中从左向右依次结合和计算。

例:

#include <stdio.h>

int main()
{
	int a = 0, b = 1, m = 1, n = 0;
	(m = a > b) && (n = a < b);//&&有从左向右的结合性,这里>优先级大于=,表示将a > b的值赋给m,这里a > b为假,返回0,所以m为0,但是&&有短路性质,所以后面的表达式不会进行,所以n还是0
	printf("m = %d\nn = %d\n", m, n);
	return 0;
}

2、||

称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。

(1 || 0) 为真。

(1)短路性质:如果||运算符前的表达式为真(即非0),那么整个逻辑表达式的结果已经确定为真,编译器将不会计算||后面的表达式。这被称作“短路”,因为计算过程在确定结果后就停止了,不再继续执行后面的表达式。

(2)结合性||运算符具有左结合性,这意味着在没有括号改变计算顺序的情况下,从左向右依次对表达式进行逻辑或运算。

#include <stdio.h>

int main()
{
	int a = 0, b = 1, m = 1, n = 0;
	(n = a < b) || (m = a > b);//||有从左向右的结合性,这里>优先级大于=,表示将a < b的值赋给n,这里a < b为真,返回1,所以n为1,但是||有短路性质,所以后面的表达式不会进行,所以m还是1
	printf("m = %d\nn = %d\n", m, n);
	return 0;
}

3、!

称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。

!(1 && 0) 为真。

#include <stdio.h>

int main()
{
	if (1 && 0)
	{
		printf("1 && 0 ture\n");
	}
	if (1 || 0)
	{
		printf("1 || 0 ture\n");
	}
	if (!(1 && 0))
	{
		printf("!(1 && 0) ture\n");
	}
	return 0;
}

四、位运算符

位操作符的操作对象都是二进制位。整数数据在计算机中存储是以补码的形式存储的,位操作符的操作对象是补码。

1、<<(左移操作符)

整数数据在计算机中存储是以补码的形式存储的,所以移位操作符的操作对象也是补码,相关致死可以看我之前的文章《C语言——原码,反码,补码-CSDN博客》。

左移操作符的规则:

将操作数的所有位向左移动指定的位数。左移 n 位相当于乘以 2 的 n 次方。

二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

例1:正数左移

int a = 7;
int b = a << 1;
printf("%d\n",b);

左移操作不会不会对原来的变量造成改变。

将7左移一位,正数的原码、反码、补码是一样的

最后变成

转换为十进制为14。

例2:负数左移

	int a = -7;
	int b = a << 1;
	printf("%d\n",b);

将-7左移一位

最后变成

转化为原码

转换为十进制为-14

2、>>(右移操作符)

右移操作符相对与左移操作符有些复杂,因为它有两种移位方式:

(1)逻辑移位

右边丢弃,左边补零。

例1:正数逻辑右移

正数7

转化为十进制为3

例2、负数逻辑右移

负数-7

原码

补码

逻辑右移

变成

因为符号位为0,检测为正数,正数的补码与原码一致

转化为十进制为

(2)算数移位:

将操作数的所有位向右移动指定的位数。如果是偶数右移n位相当于除以 2 的 n 次方,如果是奇数,向下取偶后除以2的n次方。二进制右移运算符。将一个数的各二进制位全部右移若干位,左补原符号位(即正数左补 0,负数左补 1),右边丢弃。

大多数编译器使用的是算数右移。

例3:正数算数右移

正数7

右移左边补零

变为

转化为十进制为3

7向下取偶后除以2,6?/ 2 = 3

例4、负数算数右移

负数-7

原码

补码

右边丢弃,左边补1

变成

符号位为1,检测为负数,转换为原码为

转化为十进制为-4

-7向下取偶后除以2,-8 / 2 = -4

3、& (按位与)AND

对两个操作数的每一位执行逻辑与操作,如果两个相应的位都为 1,则结果为 1,否则为 0。
按位与操作,按二进制位进行"与"运算。运算规则:

0&0=0; ??
0&1=0; ? ?
1&0=0; ? ??
1&1=1;

例:3 & -7

转换为补码

进行按位与操作

因为结果为正数,结果转换为十进制为1

	int b = 3 & -7;
	printf("%d\n",b);

4、| (按位或)OR

对两个操作数的每一位执行逻辑或操作,如果两个相应的位其中有一个为1或两个都为1,则结果为1,否则为0。
按位或运算符,按二进制位进行"或"运算。运算规则:

0|0=0;   
0|1=1;   
1|0=1;    
1|1=1;

例:3 | -7

转换为补码

进行按位或操作

由于结果为负数,将结果转换为原码

结果转换为十进制为-5

	int b = 3 | -7;
	printf("%d\n",b);

5、^ (按位异或)XOR

对两个操作数的每一位执行逻辑异或操作,如果两个相应的位值相同,则结果为 0,否则为 1。
异或运算符,按二进制位进行"异或"运算。运算规则:

0^0=0;   
0^1=1;   
1^0=1;  
1^1=0;

(1)例:3 ^?-7

转换为补码

进行按位异或操作

由于结果为负数,将结果转换为原码

结果转换为十进制为-6

	int b = 3 ^ -7;
	printf("%d\n",b);

(2)异或的特点

(i)a ^ a = 0

3 ^ 3 = 0

其实任意整数自己对自己异或的结果都是0,即a ^ a = 0。

(ii)0?^ a = a

0 ^ 3 = 3

其实0对任意整数进行异或操作结果都是此整数,即0 ^ a = a。

(iii)交换律a ^ b ^ c = b?^ a ^ c = c ^ b ^ a = a ^ c ^ b

2 ^ 3?^ 4 =

3 ^ 2 ^ 4=

4 ^ 3 ^ 2=

2 ^ 4 ^ 3

	int a = 2 ^ 3 ^ 4;
	int b = 3 ^ 2 ^ 4;
	int c = 4 ^ 3 ^ 2;
	int d = 2 ^ 4 ^ 3;
	printf("%d %d %d %d\n",a,b,c,d);

(3)例:不创建临时变量,交换两个数

#include <stdio.h>

int main()
{
	int a = 3, b = 7;
	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);
	return 0;
}

#include <stdio.h>

int main()
{
	int a = 3, b = 7;
	printf("交换之前a = %d,b = %d\n", a, b);
	a = a ^ b;//得到a的值为3 ^ 7
	b = a ^ b;//得到b的值为3 ^ 7 ^ 7,符合交换律,交换成7 ^ 7 ^ 3,由于a ^ a = 0,所以结果为3,这时b的值为3
	a = a ^ b;//得到a的值为3 ^ 7 ^ 3,符合交换律,交换为3 ^ 3 ^ 7,由于a ^ a = 0,所以结果为7,这时a的值为7
	//完成交换
	printf("交换之后a = %d,b = %d\n", a, b);
	return 0;
}

这种方法只适用于整数,浮点数无法进行位操作。而且这种操作是用时间换空间,操作需要的算力较大,时间较长,虽然节省了空间,但是没有直接创建临时变量更好。

6、~ (按位取反)

对操作数的每一位执行逻辑取反操作,即将每一位的 0 变为 1,1 变为 0。
取反运算符,按二进制位进行"取反"运算。运算规则:

~00000001得到11111110

对整数3按位取反

进行取反操作

结果为负数,转换为原码

转换为十进制为-4

位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:

pqp & qp | qp ^ q
00000
01011
11110
10011

五、赋值操作符

简单赋值操作符

1、=

赋值操作符,简单的赋值运算符,把右边操作数的值赋给左边操作数。

a = 1;

C语言支持连续赋值,对于下面的几种赋值操作符也可以连续赋值。

	a = b = 1;

从右向左运算,将1赋值给b,将b赋值给a。

但是不建议使用,在调试是无法看到具体过程,而且可读性较低。

复合赋值操作符

2、+=

加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数。

C += A 相当于 C = C + A

3、-=

减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数。

C -= A 相当于 C = C - A

4、*=

乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数。

C *= A 相当于 C = C * A

5、/=

除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数。

C /= A 相当于 C = C / A

6、%=

求模且赋值运算符,求两个操作数的模赋值给左边操作数。

C %= A 相当于 C = C % A

7、<<=

左移且赋值运算符。

C <<= 2 等同于 C = C << 2

8、>>=

右移且赋值运算符。

C >>= 2 等同于 C = C >> 2

9、&=

按位与且赋值运算符。

C &= 2 等同于 C = C & 2

10、|=

按位或且赋值运算符。

C |= 2 等同于 C = C | 2

11、^=

按位异或且赋值运算符。

C ^= 2 等同于 C = C ^ 2

六、其他操作符

1、-(负号)

与数学上的负号作用相似。

	int a = 1;
	int b = -1;
	printf("-a = %d\n-b = %d\n", -a, -b);

2、+(正号)

与数学上的正号作用相似。

	int a = 1;
	int b = -1;
	printf("+a = %d\n+b = %d\n", +a, +b);

3、&(取地址操作符)

取出一个变量在内存中的地址。

	int a = 1;
	printf("a的地址是 %p\n", &a);

4、sizeof()

sizeof是用来计算变量(或类型)所占内存空间大小,不关注内存中的存储内容,单位是字节。

printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(float));
printf("%zu\n", sizeof(char));

sizeof输出的数据类型是size_t,格式字符串可用%zu或%zd。

在用sizeof求变量的大小时,可以不带括号,但求类型时不能不带括号。

	int i = 6;
	printf("%zu\n", sizeof i);

由此也可以证明sizeof是操作符,不是函数。

5、*间接访问操作符(解引用操作符)

通过一个地址找到地址指向的变量。

int i = 0;
int* p = &i;//p为指向i的指针变量
*p = 1;//通过p指针找到i变量,对i变量操作,将1赋值给i
printf("%d", i);

6、(类型)强制转换类型

将数字或变量强制转换为一种类型。。

	int i = 6.6;

可改成

	int i = (int)6.6;

7、? : (条件操作符)三目操作符

表达式1 ? 表达式2 : 表达式3

表达式1为真时,表达式2执行,表达式3不执行,整个表达式的结果为表达式2的结果;表达式1为假时,表达式2不执行,表达式3执行,整个表达式的结果为表达式3的结果。

例:

#include <stdio.h>

int main()
{
	int a = 0, b = 1;
	int max = a > b ? a : b;
	int min = a < b ? a : b;
	printf("max = %d\nmin = %d\n", max, min);
	return 0;
}

8、exp1 , exp2, ... , expn(逗号表达式)

逗号表达式是用逗号隔开的一系列表达式,从左向右依次执行,表达式的结果是最后一个表达式的结果。

	int a = 0, b = 1,c = 0;
	int d = (a = 2, b = a + 1, c = b);
	printf("%d %d %d %d\n", a, b, c, d);

9、[](下标引用操作符)

[]的两个操作数是数组名和索引值,使用索引值和数组名可以找到这个索引对应的数据。

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", arr[0]);//索引为0的数据为1

这里有个很好玩的,既然[]的操作数是两个,那例如3 + 2中的+是操作符,而它的操作数3和2可以交换,即3 + 2 等价于?2 + 3,那arr[0]可不可以写成0[arr]呢,答案是可以的。

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", 0[arr]);//索引为0的数据为1

这更能证明它的操作数是两个。

10、()(函数调用操作符)

使用可以进行对函数进行调用,接收多个操作数,其中一个是函数名,其他的是传给函数的实参。

#include <stdio.h>

void Print(int x)
{
	printf("%d\n", x);
}

int main()
{
	int a = 0;
	Print(a);//这里的()就是函数调用操作符
	return 0;
}

11、. 点操作符(结构成员访问操作符)

用于直接访问结构体变量或联合体变量的成员。当你有一个结构体变量,而不是指针时,你会使用点操作符来访问其成员。

#include <stdio.h>

struct Student
{
	char name[10];
	int age;
	int high;
};
int main()
{
	struct Student z = { "张三",18,180};
	printf("姓名 %s\n年龄 %d\n身高 %d\n", z.name, z.age, z.high);
	return 0;
}

12、-> 箭头操作符(结构成员访问操作符)

用于通过结构体指针访问其成员。当你有一个指向结构体的指针而不是结构体变量本身时,你会使用箭头操作符来间接访问其成员。

#include <stdio.h>

struct Student
{
	char name[10];
	int age;
	int high;
};
int main()
{
	struct Student z = { "李四",19,190};
	struct Student* pz = &z;
	printf("姓名 %s\n年龄 %d\n身高 %d\n", pz->name ,pz->age ,pz->high);
	return 0;
}

七、操作符优先级

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

文章来源:https://blog.csdn.net/stewie6/article/details/135307863
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。