1、算数操作符
+ - * / %
这里提一下 / 和 % :
/和数学里的除号运算方式不太一样。C语言中,整数/整数,得到的结果还是整数。要想得到的结果为实数,/两端的数据至少一个为实数类型。
int a = 1 / 2;
printf("%d\n", a);//显示结果为0
double b = 1.0 / 2;
printf("%lf\n", b);//显示结果为0.500000
%是取模操作符,就是求余数。%操作符的两个操作数必须是整数,即整数%整数。
int a = 3 % 2;
printf("%d\n", a);//显示结果为1
int b = 3.0 % 2;//error
2、移位操作符
<< 左移操作符 >> 右移操作符
<< 左移操作符规则:左边丢弃,右边补0
int a = 2;
int b = a << 1;
printf("%d\n", b);//显示结果为4
左移操作符的规则通过图示比较好理解,但是右移操作符的规则与左移操作符的规则不完全一样。在介绍之前先了解一下原码、反码、补码的概念。
了解原码、反码、补码的概念后,接下来介绍右移操作符的规则:
>>右移操作符规则:
1、算数右移:右边丢弃,左边补原符号位
2、逻辑右移:右边丢弃,左边补0 (到底是算术右移还是逻辑右移,取决于编译器)
int a = -1;
int b = a >> 1;
printf("%d\n", b);//显示结果为-1
(vs2022采用的是算数右移)
由于了解了原码、反码、补码,可以知道左移操作符、右移操作符移动的都是数的补码,而输出的是数的原码,所以在进行移位操作后别忘了将补码转换为原码。
注意:
int a = -1;
int b = a >> 1;
printf(“%d\n”, b);//显示结果为-1
(这里对a进行右移操作,只是把得到的结果赋给b,a本身并不发生值的变化。)
3、位操作符
& 按位与 | 按位或 ^ 按位异或
& 按位与:两个条件同时为真(1),结果才为真(1),条件中有一个为假(0),结果就为假(0)
int a = 3;
int b = 5;
int c = a & b;
printf("%d\n", c);//显示结果为1
| 按位或:两个条件有一个为真(1),结果就为真(1),两个条件都为假(0),结果才为假(0)
int a = 3;
int b = 5;
int c = a | b;
printf("%d\n", c);//显示结果为7
^ 按位异或:两个条件一真(1)一假(0),结果才为真(1),就是对应的二进制位,相异为真(1),相同为假(0)。
int a = 3;
int b = 5;
int c = a ^ b;
printf("%d\n", c);//显示结果为6
4、赋值操作符
=
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
符合赋值符的概念很简单,但是作用很大。这里简单提一下,以 += 为例:
int a = 10;
a = a + 100;//1
a += 100; //2 1和2是等价的
其他复合赋值符也类似。
注意:
=是赋值;==是判断,判断是否相等。
5、单目操作符
! 逻辑反操作 - 负值 + 正值 & 取地址 sizeof 求操作数的类型长度(以字节为单位) ~ 对一个数的二进制按位取反 -- 前置、后置-- ++ 前置、后置++ * 间接访问操作符(解引用操作符) (类型) 强制类型转换
! 逻辑反操作
逻辑反操作符就是把真变假,把假变真。
int a = 5;
//a为真,打印hehe
if (a)
{
printf("hehe\n");
}
//a为假,打印haha
if (!a)
{
printf("haha\n");
}
//显示结果为hehe
补充:德摩根定律:对各条件取非,然后将逻辑与变为逻辑或、逻辑或变为逻辑与,然后再取否定,结果和原条件一样,即:
x && y和! (!x || !y)相等
x || y和! (!x && !y)相等
sizeof 求操作数的类型长度(以字节为单位)
int a = 10;
//计算a所占内存空间的大小,单位是字节
printf("%d\n", sizeof(a));//显示结果为4
//计算int类型的大小,单位是字节
printf("%d\n", sizeof(int));//显示结果为4
int arr[10] = { 0 };
//计算整个数组所占内存空间的大小,单位是字节
//数组名arr单独放在sizeof,代表的是整个数组
printf("%d\n", sizeof(arr));//显示结果为40
//计算数组类型int [10]的大小,单位是字节
printf("%d\n", sizeof(int [10]));//显示结果为40
sizeof是一个操作符,不是函数!
这里思考一个代码:
short s = 5;
int a = 10;
printf("%d\n", sizeof(s = a + 2));//显示结果为2
printf("%d\n", s);//显示结果为5
解释:
第一个显示结果:s是short型,只占2个字节,即使a+12的结果是int型,但s所占的内存空间只有2个字节,所以sizeof所测的数据类型由s决定,s是short类型,占2个字节,所以显示结果为2。
第二个显示结果:放在sizeof()里面的表达式是不参与运算的。我们编写的源文件(test.c)变成可执行程序(test.exe)需要经过编译、链接、运行,sizeof()中的s=a+2在编译期间已经处理过了,所以在运行期间s=a+2这个表达式不会进行计算,因此a+2的值不会赋给s,由于a+10的值没有赋给s,所以显示结果为5。
~ 对一个数的二进制(补码)按位取反
int a = -1;
int b = ~a;
printf("%d\n", b);//显示结果为0
- - 前置、后置- -
++ 前置、后置++
++前置:先++,再使用
后置++:先使用,再++
(- -前置、后置- -同理)
int a = 10;
int b = ++a;
printf("%d\n", b);//显示结果为11
printf("%d\n", a);// 显示结果为11
int a = 10;
int b = a++;
printf("%d\n", b);//显示结果为10
printf("%d\n", a);// 显示结果为11
int a = 10;
printf("%d\n", a--);//显示结果为10
printf("%d\n", a);//显示结果为9
这里吐槽一个(妖魔化的)代码:
结果就不说了,不同的编译器结果是不一样的。
& 取地址操作符
* 解引用操作符
1| int a = 10;
2| int* pa = &a;
3| *pa = 20;
4| printf("%d\n", a);//显示结果为20
代码解释:
第2行:&a是把a的地址取出来,int的表明pa是一个指针变量,不是解引用操作符的意思,所以这行代码的意思是用&把a的地址取出来,通过=把地址存进指针变量pa里。
第3行:*pa中的*是解引用操作符,*pa的意思是通过pa中存储的地址找到它所指向的对象,也就是a。
(类型) 强制类型转换
int a = (int)3.14;
printf("%d\n", a);
3.14是double型, 如果要把3.14强制放到int型的a里面的话会有警告,而(int)3.14把3.14强制转换成int型,这时候把值存进a里就不会报警告了。
通过上面的介绍,发现这些操作符的操作数只有一个,所以叫单目操作符,因此单目操作符中的 + -表示的是数值的正负,而双目操作符里的+ -表示的是加减运算。
6、关系操作符
> >= < <= != 用于判断是否不相等 == 用于判断是否相等
再次强调:
=是赋值; ==是判断,判断是否相等。
7、逻辑操作符
&& 逻辑与 || 逻辑或
&&逻辑与表示的意思是并且,||逻辑或表示的意思是或者。这两个操作符用于表示逻辑上的真假。
a && b只有a、b同时为真,逻辑结果才为真。
int a = 3;
int b = 0;
if (a && b)
printf("逻辑为真\n");//不打印
a || b只要有一个为真,逻辑结果就为真。
int a = 3;
int b = 0;
if (a || b)
printf("逻辑为真\n");//显示结果为逻辑为真
这里思考两个代码:
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a=%d b=%d c=%d d=%d\n", a, b, c, d);
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("a=%d b=%d c=%d d=%d\n", a, b, c, d);
//第一个代码的显示结果为a=1 b=2 c=3 d=4
//第二个代码的显示结果为a=1 b=3 c=3 d=4
解释:
逻辑运算符会改变运算的顺序。
例1:
expr1 && expr2 && expr3,当表达式expr1结果假时,整个expr1 && expr2 && expr3的结果已经为假,所以表达式expr2、expr3不会有机会进行运算,也就是不执行这两个表达式;当表达式expr1为真时,expr2就有机会参与运算并判断真假。
例2:
expr1 || expr2 || expr3,当表达式expr1结果真时,整个expr1 && expr2 && expr3的结果已经为真,所以表达式expr2、expr3不会有机会进行运算,也就是不执行这两个表达式;当表达式expr1为假时,expr2就有机会参与运算并判断真假。
8、条件操作符
表达式1 ?表达式2 :表达式3
条件操作符又叫三目操作符,它是唯一一个有三个操作数的操作符。那这个操作符是什么意思呢?
expr1 ? expr2 : expr3
解释:首先对expr1的结果进行判断,如果判断结果为真,直接执行expr2,跳过expr3;如果判断结果为假,直接执行expr3,跳过expr2。
int a = 3;
int b = 0;
//代码1 if语句
if (a > 5)
b = 1;
else
b = -1;
//代码2 条件操作符
b = (a > 5 ? 1 : -1);
代码1和代码2表示的意思是一样的。
9、逗号表达式
表达式1,表达式2,表达式3,··· 表达式n
逗号表达式,要从左向右依次计算。整个表达式的结果是最后一个表达式的结果。
int a = (1, 2, 3, 4);
printf("%d\n", a);//显示结果为4
这里再提供一个代码和它的运行结果,代码比较简单,就不做解释了。
10、下标引用、函数调用和结构成员
[ ] 下标引用操作符
所谓的下标引用操作符,就是通过下标来访问数组里的元素。
操作数:一个数组名 + 一个索引值
1| int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
2| printf("%d\n", arr[4]);//显示结果为5
第一行代码中的[ ]是在定义数组的时候指定数组大小的一种语法格式;
第二行代码中的[ ]就是我们的下标引用操作符。
( ) 函数调用操作符
( ) 函数调用操作符在我们的代码中是非常常见的,无论是最常见的main函数(主函数),或者是库函数,自定义函数等,都会有( )的身影。
( ) 函数调用操作符接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int ret = Add(a, b);
printf("%d\n", ret);//显示结果为30
return 0;
}
访问一个结构的成员:
.
->
. 的使用形式:结构体变量名.成员名
-> 的使用形式: 结构体指针->成员名
(1)代码演示 . 的使用形式:
struct Book
{
char name[20];
char id[20];
int price;
};
int main()
{
struct Book b = { "C语言", "20221002", 55 };
printf("书名:%s\n", b.name);//显示结果为 书名:C语言
printf("书号:%s\n", b.id);//显示结果为 书号:20221002
printf("定价:%d\n", b.price);//显示结果为 定价:55
return 0;
}
(2)代码演示 -> 的使用形式:
struct Book
{
char name[20];
char id[20];
int price;
};
int main()
{
struct Book b = { "C语言", "20221002", 55 };
struct Book* pb = &b;
printf("书名:%s\n", pb->name);//显示结果为 书名:C语言
printf("书号:%s\n", pb->id);//显示结果为 书号:20221002
printf("定价:%d\n", pb->price);//显示结果为 定价:55
return 0;
}