【C语言】操作符(运算符)详解(非常完整,涵盖各种类的操作符,每个操作符都有示例演示)
梓䈑 2024-10-08 14:05:02 阅读 63
文章目录
前言一、操作符分类二、算术操作符1. + 、 - 和 *2. /3. %
三、移位操作符1.原码、反码、补码2. 左移操作符 <<3.右移操作符 >>
四、位操作符五、赋值操作符1.复合赋值符
六、单目操作符1. ++、- -1.1 前置++和后置++1.2 前置 - - 和后置 - -
2. +、-3. &和*4. sizeof操作符5. 强制类型转换操作符
七、关系操作符八、逻辑操作符九、条件操作符十、逗号表达式十一、下标访问操作符[ ]十二、函数调用操作符()十三、结构成员访问操作符1. 结构体2. . 和 ->
十四、操作符的属性:优先级、结合性
前言
一、下文会叙述到的所有操作符的汇总和分类
二~十三、对各种操作符展开介绍及大量示例演示使用效果
十四、含重要的操作符优先级、结合性的整合表格,十分清晰好用
一、操作符分类
• 移位操作符: << >>
• 位操作符: & | ^ ~
• 赋值操作符: = += -= *= /= %= <<= >>= &= |= ^=
• 单目操作符:! ~ ++ -- + - & * sizeof (类型)
• 关系操作符: > >= < <= == !=
• 逻辑操作符: && || !
• 条件操作符: ? :
• 逗号表达式: ,
• 下标引⽤: []
• 函数调用: ()
• 结构成员访问: . ->
二、算术操作符
C语⾔中进行简单运算的操作符叫做算术操作符,分别是: + 、- 、* 、/ 、 % ,这些操作符都是有2个操作数的,位于操作符两端的就是它们的操作数,这种操作符也叫双目操作符。
1. + 、 - 和 *
操作符+ 、 - 和 * ⽤来完成加法、减法和乘法。
#include <stdio.h>
int main()
{
printf("%d\n", 5 + 2);
printf("%d\n", 5 - 2);
printf("%d\n", 5 * 2);
return 0;
}
2. /
操作符 / 用来完成除法。
除号两端的操作数都是整数,执行的就是整数除法,得到的结果也是整数。
除号两端的操作数只要有⼀个是浮点数,执行的就是浮点数除法,得到的结果就是浮点数。 如下:
<code>#include <stdio.h>
int main()
{
double i = 6 / 5;//除号两端操作数都是整数,执行整数除法
double j = 6 / 5.0;//除号两端的操作数有⼀个是浮点数,执行浮点数除法
int k = 6 / 5;
printf("%lf\n", i);
printf("%lf\n", j);
printf("%d\n", k);
return 0;
}
上面例子中,尽管变量 i 的类型是 double(浮点数),但是 6 / 5 得到的结果是 1 ,而不是1.2 。原因就在于 C 语言里面的整数除法是整除,只会返回整数部分,丢弃小数部分。
而 6.0 / 5 表示进行浮点数除法,得到的结果就会是 1.2 。
3. %
运算符 % 表示求模(余)运算,即返回两个整数相除的余数。这个操作符只能用于整数,不能用于浮点数。 示例如下:
<code>#include <stdio.h>
int main()
{
printf("%d\n", 10 % 3);
printf("%d\n", 20 % 6);
printf("%d\n", 25 % 7);
return 0;
}
负数求模的规则是,结果的正负号由第一个运算数的正负号决定。 示例如下:
<code>#include <stdio.h>
int main()
{
printf("%d\n", -10 % 3);
printf("%d\n", 20 % -6);
printf("%d\n", -25 % -7);
return 0;
}
三、移位操作符
移位操作符的操作数只能是整数。
对于整数来说,数据存放在内存中的其实是它的补码,所以移位操作符其实是对整数在内存中存储的补码形式来进行操作的,所以要学习这类操作符之前我们得先了解整数在内存中的存储形式。
1.原码、反码、补码
整数的2进制表示方法有三种,即原码、反码和补码。
有符号整数的三种表示⽅法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号位,剩余的都是数值位。符号位都是用0表示“正”,用1表示“负”。
正整数的原、反、补码都相同。负整数的三种表示方法各不相同, 如下所示:
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
补码得到原码也是可以使用:取反,+1的操作。
有符号整数原、反、补码的转换,示例如下:
<code>int a = 1;//int型整数在内存中占4字节,也就是32bit
原码:00000000 00000000 00000000 00000001//最高位是符号位,正整数的符号位用‘0’表示
//正整数的原、反、补码都相同,所以无需转换
int b = -1;
原码:10000000 00000000 00000000 00000001//最高位是符号位,负整数的符号位用‘1’表示
反码:11111111 11111111 11111111 11111110//原码符号位不变,数值位按位取反,得到反码
补码:11111111 11111111 11111111 11111111//反码+1,得到补码
2. 左移操作符 <<
左移操作符 <<的作用是对整型变量的补码左移指定位数,它的移位规则是:左边抛弃、右边补0。 它的操作数有2个,是一个双目操作符。
左移操作符 <<的使用示例如下:
#include <stdio.h>
int main()
{
int n = 1;
n = n << 1;//1的补码是00000000000000000000000000000001
//左移1位变成:00000000000000000000000000000010(正整数的原、反、补码都相同,所以这也是结果的原码,转换成10进制就是2)
printf("%d\n", n);
int i = -1;
i = i << 1;//-1的补码是11111111111111111111111111111111
//左移1位变成:11111111111111111111111111111110(这是结果的补码)
//对结果的补码进行取反,+1的操作得到它的原码:10000000000000000000000000000010(转换成10进制就是-2)
printf("%d\n", i);
return 0;
}
3.右移操作符 >>
右移操作符 >>也是一个双目操作符,它的作用是对整型变量的补码右移指定位数, 不过它的移位规则分为两种:
(1)逻辑右移:左边用0填充,右边丢弃
(2)算术右移:左边用原该值的符号位填充,右边丢弃(绝大部分编译器都会采用算数右移的规则,后面的举例也采用算术右移)
右移操作符 >>的使用示例如下:
<code>#include <stdio.h>
int main()
{
int n = 1;
n = n >> 1;//1的补码是00000000000000000000000000000001
//右移1位变成:00000000000000000000000000000000(正整数的原、反、补码都相同,所以这也是结果的原码,转换成10进制就是0)
printf("%d\n", n);
int i = -1;
i = i >> 1;//-1的补码是11111111111111111111111111111111
//右移1位仍然是:11111111111111111111111111111111(这是结果的补码)
//对结果的补码进行取反,+1的操作得到它的原码:10000000000000000000000000000001(转换成10进制就是-1)
printf("%d\n", i);
return 0;
}
四、位操作符
• | :按位或操作符(双目操作符)
• ^ :按位异或操作符(双目操作符)
• ~ :按位取反操作符(单目操作符)
注:他们的操作数必须是整数,而且是使用整数在内存中存储的补码形式来进行位操作的。
#include <stdio.h>
int main()
{
int num1 = 1;
int num2 = -1;
printf("%d\n", num1 & num2);//1的补码:00000000000000000000000000000001
//-1的补码:11111111111111111111111111111111
//1&-1的结果:00000000000000000000000000000001(每个对应的bit位,都为1则该位结果为1,否则为0)
//结果的补码是正整数,正整数的原、反、补码都相同,所以这也是结果的原码,转换成10进制就是1)
printf("%d\n", num1 | num2);//1的补码:00000000000000000000000000000001
//-1的补码:11111111111111111111111111111111
//1|-1的结果:11111111111111111111111111111111(每个对应的bit位,有1则该位结果为1,都为0结果才为0)
//对结果的补码进行转换得到其原码:10000000000000000000000000000001(转换成10进制就是-1)
printf("%d\n", num1 ^ num2);//1的补码:00000000000000000000000000000001
//-1的补码:11111111111111111111111111111111
//1^-1的结果:11111111111111111111111111111110(每个对应的bit位,相异则该位结果为1,相同则为0)
//对结果的补码进行转换得到其原码:10000000000000000000000000000010(转换成10进制就是-2)
printf("%d\n", ~0);//0的补码:00000000000000000000000000000000
//~0的结果:11111111111111111111111111111111(把每一个bit位取反)
//对结果的补码进行转换得到其原码:10000000000000000000000000000001(转换成10进制就是-1)
return 0;
}
五、赋值操作符
在变量创建的时候给⼀个初始值叫初始化,在变量创建好后,再给⼀个值,这叫赋值。 示例如下:
<code>#include <stdio.h>
int main()
{
int a = 0;//变量初始化
a = 5;//赋值,这里使用的就是赋值操作符
return 0;
}
赋值操作符也可以连续赋值,如下:
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
a = b = 5;//连续赋值,从右向左依次赋值的。
return 0;
}
1.复合赋值符
C语⾔中提供了复合赋值符,方便我们编写代码,这些赋值符有:
• += -= *= /= %=
• >>= <<=
• &= |= ^=
部分复合赋值符作用见如下示例(其余类似):
#include <stdio.h>
int main()
{
int a = 0;
a += 5;//效果等同于"a=a+5"
printf("%d\n", a);
a -= 3;//效果等同于"a=a-3"
printf("%d\n", a);
a *= 2;//效果等同于"a=a*2"
printf("%d\n", a);
return 0;
}
六、单目操作符
<code>• !: 逻辑取反操作符(在逻辑操作符部分展开介绍)
• ~ :按位取反操作符(在位操作符部分已展开介绍)
• ++ :自增操作符
• -- :自减操作符
• + :正号操作符
• - :负号操作符
• & :取地址操作符
• * :解引⽤操作符
• sizeof :长度计算操作符
• (类型) :强制类型转换操作符
1. ++、- -
++是⼀种自增的操作符,⼜分为前置++和后置++;- -是⼀种⾃减的操作符,也分为前置- -和后置- -.
1.1 前置++和后置++
它们相同的使用效果是都能使变量的值加1,例如:
#include <stdio.h>
int main()
{
int a = 5;
int b = 5;
++a;//a的值增加1
b++;//b的值增加1
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
它们使用时的区别在于:前置++是先+1,后使用;后置++是先使用,后+1。 示例如下:
<code>#include <stdio.h>
int main()
{
int a = 5;
int b = ++a;//a先+1,再将变化后的a值赋给b
printf("a=%d b=%d\n", a, b);
int c = 5;
int d = c++;//先将c的值赋给d,然后c再+1
printf("c=%d d=%d\n", c, d);
return 0;
}
1.2 前置 - - 和后置 - -
它们相同的使用效果是都能使变量的值减1。
它们使用时的区别在于:前置- -是先-1,后使用;后置- -是先使用,后-1。 示例如下:
<code>#include <stdio.h>
int main()
{
int a = 5;
int b = --a;//a先-1,再将变化后的a值赋给b
printf("a=%d b=%d\n", a, b);
int c = 5;
int d = c--;//先将c的值赋给d,然后c再-1
printf("c=%d d=%d\n", c, d);
return 0;
}
2. +、-
单目操作符里的+是正号,-是负号。
操作符 + 对正负值没有影响,是⼀个完全可以省略的运算符,如下:
int a = +5; //等价于 int a = 5;
操作符 - 可以用来改变⼀个值的正负性,负数的前面加上 - 就会得到正数,正数的前面加上 - 会得到负数,使用如下:
<code>#include <stdio.h>
int main()
{
int a = 5;
int b = -5;
printf("%d\n", -a);
printf("%d\n", -b);
return 0;
}
3. &和*
取地址操作符&和解引用操作符 * 的使用示例如下:
<code>#include <stdio.h>
int main()
{
int n = 5;
int* p = &n;//这里的&就是取地址操作符,它的作用是取出n的地址并存储到指针变量p中
*p = 10;//这里的*就是解引用操作符.*p的意思就是通过p中存放的地址,找到指向的空间,*p其实就是n变量了,所以*p = 10,就是把n改成了10.
printf("%d\n", n);
return 0;
}
注:指针变量也是⼀种变量,这种变量就是专门用来存放地址的,存放在指针变量中的值都会理解为地址。
以上代码中p的类型是 int* , * 是在说明p是指针变量,而前面的 int 是在说明指针变量p指向的是整型(int)类型的对象。
4. sizeof操作符
sizeof 是⼀个专门用来计算数据类型⻓度的操作符,单位是字节。
sizeof 操作符的操作数可以是类型,也可是变量(计算变量的类型的长度)或者表达式(计算表达式结果的类型长度),形式如下:
sizeof( 类型 )
sizeof(表达式)
sizeof 表达式
sizeof(变量)
sizeof 变量
注:sizeof 的操作数如果不是类型,是表达式或者变量的时候,是可以省略掉后边的括号的。但由于操作符优先级的缘故,有些时候省略掉sizeof后面表达式的括号,可能就会出现问题。
sizeof 的计算结果是 size_t 类型的。
注:size_t 类型是一种无符号整数类型,但它在不同的系统上的定义可能不太一样,size_t在32位系统上定义为 unsigned int,在64位系统上定义为 unsigned long。我们一般使用 %zd格式 来打印size_t类型的数据。
sizeof操作符的使用如下:
<code>#include <stdio.h>
int main()
{
int a = 5;
double b = 0.0;
printf("%zd\n", sizeof a);//a的类型是int,int类型的大小是4字节
printf("%zd\n", sizeof b);//b的类型是double,double类型的大小是8字节
printf("%zd\n", sizeof(a = 8 + 5));//把8+3的值赋给a,表达式的最终结果就是a,a的类型是int,int类型的大小是4字节
printf("%zd\n", sizeof(int));//int类型的大小是4字节
printf("%zd\n", sizeof(char));//char类型的大小是1字节
printf("%zd\n", sizeof(double));//double类型的大小是8字节
return 0;
}
注:sizeof 后边的表达式是不真实参与运算的,根据表达式结果的类型来得出大小。 示例如下:
<code>#include <stdio.h>
int main()
{
int a = 5;
printf("%zd\n", sizeof(a = 8 + 5));//sizeof后边的表达式把8+3的值赋给a
printf("%d\n", a);//a的值仍然为5,所以sizeof后边的表达式是不真实参与运算的,也就不会影响a的值
return 0;
}
5. 强制类型转换操作符
在操作符中还有⼀种特殊的操作符是强制类型转换操作符,形式如下:
(类型)
请看代码片段演示使用效果:
<code>int a = 3.14;
//a是int类型, 3.14是double类型,两边的类型不⼀致,编译器会报警告
为了消除这个警告,我们可以使用强制类型转换:
int a = (int)3.14;
//意思是将3.14强制类型转换为int类型,这种强制类型转换只取整数部分,也就是只把3.14的整数部分3赋给a
七、关系操作符
C语言中的关系操作符其实是对两个数值或数值表达式进行比较,判断其比较的结果是否符合给定条件。例如:a>5 是一个 关系表达式,> 是一个关系操作符,如果a的值是10,满足 a>5 的条件,因此关系表达式的值为“真”,表达式的值为1;如果a的值是2,不满足 a>5 的条件,关系表达式的值则为“假”,表达式的值为0。
注:C语言中,非0表示真,0表示假
C语言提供6种关系操作符,如下:
>(大于) >=(大于等于) <(小于) <=(小于等于)
==(等于) !=(不等于)
注:相等操作符 == 与赋值操作符 = 是两个不一样的操作符,不要混淆。
部分关系操作符的使用,示例如下:
#include <stdio.h>
int main()
{
int a = 5;
printf("%d\n", a > 4);
printf("%d\n", a < 4);
printf("%d\n", a == 4);
printf("%d\n", a != 4);
return 0;
}
八、逻辑操作符
逻辑操作符提供逻辑判断功能,⽤于构建更复杂的表达式,主要有下面三个操作符:
• !:逻辑取反操作符(改变单个表达式的真假)。
• && :逻辑与操作符,就是并且的意思(两侧的表达式都为真,则为真,否则为假)。
• || :逻辑或操作符,就是或者的意思(两侧只要有⼀个表达式为真,就为真,两侧的表达式都为假结果才为假)。
注:!是单目操作符,&&和 || 是双目操作符
用逻辑操作符将关系表达式或其他逻辑量连接起来的式子就是逻辑表达式,示例如下:
<code>#include <stdio.h>
int main()
{
int a = 5;
int b = 0;
printf("%d\n", !a);//a是非零值,表达为真;!逻辑取反,所以!a表达为假,用0表示
printf("%d\n\n", !b);//b为零,表达为假;!逻辑取反,所以!b表达为真,用1表示
printf("%d\n", (a > 3) && (a > b));//&&两侧的表达式都为真,则整个逻辑表达式为真,用1表示
printf("%d\n", (a > 5) && (a > b));//&&左侧的表达式为假,则整个逻辑表达式为假,用0表示
printf("%d\n\n", (a > 5) && (a < b));
printf("%d\n", (a > 3) || (a > b));//||两侧的表达式都为真(实际||两侧只要有⼀个表达式为真,就为真),则整个逻辑表达式为真,用1表示
printf("%d\n", (a > 5) || (a > b));//||右侧的表达式为真,则整个逻辑表达式为真,用1表示
printf("%d\n", (a > 5) || (a < b));//||两侧的表达式都为假,则整个逻辑表达式为假,用0表示
return 0;
}
九、条件操作符
条件操作符也叫三目操作符,需要接受三个操作数的, 形式如下:
exp1 ? exp2 : exp3
条件操作符的计算逻辑是:如果 exp1 为真, exp2 计算,计算的结果是整个表达式的结果;如果
exp1 为假, exp3 计算,计算的结果是整个表达式的结果。
条件操作符的使用效果如下:
<code>#include <stdio.h>
int main()
{
int a = 5;
int b = 0;
int c = a > b ? a : b;//a>b为真,a的值是整个表达式的结果
int d = a < b ? (a + b) : 10;//a<b为假,10是整个表达式的结果
printf("%d\n", c);
printf("%d\n", d);
return 0;
}
十、逗号表达式
逗号表达式,就是用逗号隔开的多个表达式, 形式如下:
exp1, exp2, exp3, …expN
逗号表达式的效果就是从左向右依次执行表达式,但整个表达式的结果是最后⼀个表达式的结果,示例如下:
<code>#include <stdio.h>
int main()
{
int a = 5;
int b = 0;
int c = (b = b + a, b > 0, a = b * 3);//逗号表达式从左向右依次执行表达式,逗号表达式的结果是最后⼀个表达式 a=b*3 的结果
printf("%d\n", c);
return 0;
}
十一、下标访问操作符[ ]
下标访问操作符是专门应用于数组的操作符,用于访问数组中元素,它的操作数是:⼀个数组名 + ⼀个下标,如下:
<code>int arr[10] = { 0};//创建数组
arr[9] = 10;//使用下标引⽤操作符[],[]的两个操作数是arr和9。
下标访问操作符[ ]的使用,示例如下:
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6 };//创建及初始化数组
int i = 0;
for (i = 0; i < 6; i++)
{
printf("%d\n", arr[i]);//通过下标引⽤操作符[]及改变下标来访问数组中的每一个元素
}
return 0;
}
十二、函数调用操作符()
函数调用操作符()可以接受⼀个或者多个操作数:第⼀个操作数是函数名,剩余的操作数就是传递给函数的参数。
函数调用操作符()的示例如下:
<code>#include <stdio.h>
int add(int x, int y)
{
return x + y;
}
int main()
{
int n = add(4, 6);//这里的()就是作为函数调用操作符,它的操作数是add、4和6
printf("%d", n);//这里的()也是函数调用操作符,它的操作数是printf和n
return 0;
}
十三、结构成员访问操作符
1. 结构体
C语言已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类型还是不够的,假设我想描述学生,描述⼀本书,这时单⼀的内置类型是不行的。
比如,描述⼀个学生需要名字、年龄、学号、身高、体重等信息;描述一本书需要作者、出版社、定价等信息。所以C语言为了解决这个问题,增加了结构体这种自定义的数据类型,让程序员可以自己创造适合的类型。
结构体的定义如下:
struct tag//结构体名
{
member-list;//成员变量列表
}variable-list;//结构体变量列表
结构体是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如:标量、数组、指针,甚至是其他结构体。
结构体类型的定义及结构体变量的创建的示例如下:
#include <stdio.h>
struct Stu//创建结构体类型struct Stu
{
char name[20];//名字
int age;//年龄
char sex[10];//性别
int id;//学号
}A = { "zhangsan",18,"male",20201252 };//在创建结构体类型struct Stu的同时创建结构体变量A(全局变量)并初始化
int main()
{
struct Stu B = { "lisi",16,"female",20201214 };//创建结构体变量B并初始化
return 0;
}
2. . 和 ->
结构体成员的直接访问是通过点操作符(.)访问的, 点操作符接受两个操作数。使用方式:结构体变量.成员名,如下所示:
#include <stdio.h>
struct Stu
{
char name[20];
int age;
int id;
}A = { "zhangsan",18,20201252 };
int main()
{
struct Stu B = { "lisi",16,20201214 };
printf("%s %d %d\n", A.name, A.age, A.id);
printf("%s %d %d\n", B.name, B.age, B.id);
return 0;
}
结构体成员的间接访问是通过操作符(->)访问的,操作符 -> 接受两个操作数。使用方式:指向结构体变量的指针变量->成员名,如下所示:
<code>#include <stdio.h>
struct Stu
{
char name[20];
int age;
int id;
}A = { "zhangsan",18,20201252 };
int main()
{
struct Stu B = { "lisi",16,20201214 };
struct Stu* p1 = &A;
struct Stu* p2 = &B;
printf("%s %d %d\n", p1->name, p1->age, p1->id);
printf("%s %d %d\n", p2->name, p2->age, p2->id);
return 0;
}
十四、操作符的属性:优先级、结合性
C语言的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。
说明:
(1)优先级 指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不⼀样的。例如:表达式 3 + 4 * 5 里面既有加法运算符( + ),又有乘法运算符( * )。由于乘法的优先级高于加法,所以会先计算 4 * 5 ,而不是先计算 3 + 4 。
(2)如果两个操作符优先级相同,优先级没办法确定先计算哪个了,这时候就看 结合性 了,根据操作符是左结合,还是右结合,决定执行顺序。大部分操作符是左结合(从左到右执行),少数操作符是右结合(从右到左执行)。
(3)从上表中可以大致归纳出各类操作符优先级(以下优先级由上而下递减。),如下:
<code>•( ) [ ] —> .
• 单目操作符
• 算术操作符(* \ % + -)
• 移位操作符
• 关系操作符
• 位操作符(& ^ |,不包括 ~ )
• 逻辑操作符(&& ||,不包括!)
• 条件操作符
• 赋值操作符
• 逗号表达式
(4)圆括号的优先级最高,可以使用它改变其他运算符的优先级。例如:表达式 (3 + 4)*5 会先计算圆括号里的 3 + 4 。
下一篇: 【Python】已解决:note: This error originates from a subprocess,and is likely not a problem with pip
本文标签
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。