大家好,本文主要内容是C语言操作符的介绍,C语言有很多的操作符,学好这些操作符可以提高我们的编程水平。
我们先对操作符进行分类,随后逐一分析。
目录
操作符的分类
算数操作符
用于数学计算的操作符,分为加 +
减-
乘*
除/
取模%
移位操作符
左移操作符:<<
右移操作符: >>
位操作符
按位与 :&
按位或: |
按位异或:^
单目操作符
逻辑取反 : !
求类型长度:sizeof
+ -
取地址:&
解引用: *
关系操作符
大于和小于 :> <
大于等于 :>=
小于等于 : <=
等于 :==
不等于:!=
逻辑操作符
逻辑与: &&
逻辑或 :||
条件操作符
条件操作符(三目操作符) : ? :
逗号表达式 ,
下标引用操作符 []
函数调用操作符 :()
结构成员访问 .
和->
说完分类,下面我们开始详细介绍!
算数操作符
用于数学计算的操作符,分为加 +
减-
乘*
除/
取模%
我们主要说一下/
和%
的
/ 除法
在C语言中,除法分为整数除法和浮点数除法
1.整数除法 除号两端都是整数的时候,计算默认以整数除法的方式,也就是除得的数默认取证
下面是代码示例:
#include <stdio.h>
int main()
{
int a = 5;
int b = 2;
float c = a / b;//除号两端都是整数
printf("%f\n", c);
return 0;
}
答案不是2.5,说明这是整数除法。
2.浮点数除法 除号两端只要有一个小数,这样得出的数就是一个浮点数。
#include <stdio.h>
int main()
{
int a = 5;
float b = 2;//浮点数
float c = a / b;//除号两端有至少一个浮点数
printf("%f\n", c);
return 0;
}
%取模
取模操作符两个操作数必须均为整数,得到的结果是两数相除的余数。
比如5除以2余数是1,那么5%2=1
代码演示:
#include <stdio.h>
int main()
{
int a = 5;
int b = 2;
int c = a % b;//必须都整数
printf("%d\n", c);//打印1
return 0;
}
整除和取模的应用
在算法题中,整除和取模各自有不同作用,比如:一个整数模10,得到的数是它的最后一位;
一个整除除以10,会消去它的最后一位。这样循环下来可以取到一个整数的每一位。
移位操作符
移位移动的是二进制位,操作数只能是整数。
这里简单的说一下数据的表示方式,以后会单独去说~
数据有三种表示方式,分别是原码、反码、补码。
原码
原码是人能够直接看懂的码,是直接把十进制数转换成二进制数加上一个符号位,它的首位是符号位,0代表正数,1代表负数
比如在x86坏境中
5 表示为 00000000000000000000000000000110
-5表示为 10000000000000000000000000000110
人类读内存中数据都要转成原码去读,才能看懂。
正数的原码,反码,补码都相同,都是和原码一样!
反码
原码的符号位不变,其它位按位取反便是补码
-5的原码为 10000000000000000000000000000110
-5的反码表示为 11111111111111111111111111111001
补码
符号位不变,反码+1便是补码
接上面
-5的反码表示为 11111111111111111111111111111001
-5的补码表示为 10000000000000000000000000000010
为什么要说原码,反码补码呢?因为计算机内存中存放的就是补码!
以后会单独写博客来说这些,本篇主要还是操作符部分。
既然内存中存放的是补码,那也就说明计算机在计算的时候也是直接操作补码,移位操作符移动的就是补码的二进制位。
右移操作符 >>
右移分为两种,一种是算数右移,一种是逻辑右移。
**算数右移:**就是二进制整体向右移动,右边直接丢弃,左边补原来的符号位。
**逻辑右移:**就是二进制整体向右移动,右边直接丢弃,左边直接补0。
C语言没有明确规定到底是算数右移还是逻辑右移,一般的编译器都采用算数右移。
需要强调的是,右移左移并不会真的改变数值,比如**a>>1
,并不会真的改变a,a = a>>1
或者a >>=1
** 才会真的改变a。
算数右移的代码演示
#include<stdio.h>
int main()
{
int a = -15;
int b = a >> 1;
printf("%d\n", b);
//算数右移
//a 原码 10000000000000000000000000001111
//a 补码 11111111111111111111111111110001
//a 算数右移1位 11111111111111111111111111111000
//转换为原码 10000000000000000000000000001000
//-8 可以见得确实是算数右移
return 0;
}
左移操作符<<
对于正数左移一位效果是扩大二倍,右移一位效果是除以2。
左移代码演示:
//左移
#include<stdio.h>
int main()
{
int a = 6;
int b = a << 1;//左移一位扩大二倍
printf("%d \n", b);//输出12
return 0;
}
移位千万不要移动负位数,这是标准未定义行为,比如左移-1位,这并不代表右移一位
位操作符 & | ^
按位与 &
按位与操作符就是两个数的每一位按照补码的二进制位进行与操作。
与操作就是
0&0 = 0
0&1 = 0
1&1 = 1 只有都为1的时候结果才为1
按位与代码演示
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011 3的补码
int b = -5;
//11111111111111111111111111111011 5的补码
int c = a & b;
//按位与 全是1才为1
//00000000000000000000000000000011 3的补码
//11111111111111111111111111111011 5的补码
//00000000000000000000000000000011 3
printf("%d\n", c);//3
return 0;
}
按位或 |
和按位与类似,只不过换成了或操作
或操作就是
0&0 = 0
0&1 = 0
1&1 = 1 只有都为1的时候结果才为1
按位或代码演示
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011 3补码
int b = -5;
//11111111111111111111111111111011 5补码
int c = a | b;
//按位或 全是0才为0
//00000000000000000000000000000011 3补码
//11111111111111111111111111111011 5补码
//11111111111111111111111111111011 结果的补码
//10000000000000000000000000000101 结果的原码 -5
printf("%d\n", c);//-5
return 0;
}
按位异或 ^
按位异或就是两个二进制位相同结果为0,相异结果为1
0^0=0
0^1=1
1^0=1
1^1=0
按位异或代码演示
//位操作符
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011 3补码
int b = -5;
//11111111111111111111111111111011 5补码
int c = a ^ b;
//按位异或 相同为0 相异为一
//00000000000000000000000000000011 3补码
//11111111111111111111111111111011 5补码
//11111111111111111111111111111000 结果的补码
//10000000000000000000000000001000 结果的原码 -8
printf("%d\n", c);//-8
return 0;
}
赋值操作符
复制操作符=
,就是把右边的值传给左边,这个应该不用多说啦
可以组成复合效果的符合复制符 +=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
】
比如:
a = a + 3;
a = a += 3;
单目操作符
单目操作符就是操作数只有一个的操作符,包括逻辑反操作符,取地址操作符,解引用操作符等
逻辑反操作符 !
如果对一个大于零的数逻辑取反,它将等于0
如果对0逻辑取反,它将等于1
这有什么作用?
这对逻辑判断有帮助
如果一个值大于0,那程序默认它逻辑真true
如果一个值等于0,那程序默认它逻辑假false
逻辑取反操作符就是进行真假转换的。
取地址操作符 &
取地址操作符就是返回一个量的地址,可以用指针变量来接收。
取地址操作符代码演示
int main()
{
int a = 1;//创建一个变量a
int* pa = &a;//用取地址操作符取出a的地址,传给pa
printf("a = %d\n",a);//打印a的内容
printf("pa = %p\n",pa);//打印a的地址
return 0;
}
解引用操作符(间接访问操作符) *
解引用操作符可以通过地址找到其所指向的空间,并可以修改里面的内容
解引用操作符代码演示
int main()
{
int a = 1;//创建一个变量a 赋值为1
int* pa = &a;//用取地址操作符取出a的地址,传给pa
*pa = 10;//通过解引用操作符找到a所在的空间,更改a为10
printf("a = %d\n",a);//打印a的内容 10
return 0;
}
sizeof
这是C语言中比较特殊的操作符,使用它的格式为
sizeof(Type);
Type为数据的类型。
sizeof的用途是求一个类型所占空间的大小,注意它是操作符,不是函数!
sizeof甚至可以不用括号直接加变量名(不能直接加类型名)这足以证明它不是函数。
举例分析
int main()
{
int a = 1;//创建一个变量a 赋值为1
printf("%d\n", sizeof a);//变量a所占空间的大小,无需函数调用操作符,它不是函数
return 0;
}
当然我们在使用sizeof的时候还是要老老实实的把所求内容用括号括起来。
下面是sizeof操作符的代码演示
int main()
{
//sizeof可以直接求出一个类型所占空间的大小(单位字节)
printf("%zd\n", sizeof(char));
printf("%zd\n", sizeof(short));
printf("%zd\n", sizeof(int));
printf("%zd\n", sizeof(long));
printf("%zd\n", sizeof(long long));
printf("%zd\n", sizeof(float));
printf("%zd\n", sizeof(double));
return 0;
}
取反操作符 ~
对一个数的补码二进制位进行取反(注意这里是求得的数不是反码)
取反操作符代码演示
#include<stdio.h>
int main()
{
int a = 0;
printf("%d \n", ~a);
//00000000000000000000000000000000补码
//11111111111111111111111111111111取反
//10000000000000000000000000000001原码 -1
int b = 1;
//00000000000000000000000000000001补码
//11111111111111111111111111111110取反
//10000000000000000000000000000010原码 -2
printf("%d \n", ~b);
return 0;
}
前置++和后置++
这里只需要区分++a 和 a++的区别
前置++ 就是先使用后++
后置++ 就是先++后使用
前置++和后置++代码演示
//后置++
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
printf("b = %d\n", b);//0
b = a++;//先使用,后++
printf("b = %d\n", b);//0
b = a;
printf("b = %d\n", b);//1
return 0;
}
//前置++
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
printf("b = %d\n", b);//0
b = ++a;//先++,后使用
printf("b = %d\n", b);//1
b = a;
printf("b = %d\n", b);//1
return 0;
}
关系操作符
大于>
小于<
大于等于>=
小于等于<=
不等于!=
等于==
关系操作符一般都是用于判断,比如if语句,while语句的判断条件中。
如果为真,返回1
如果为假,返回0
关系操作符代码演示
int main()
{
int a = -1;
if (a < 0)
{
printf("a < 0\n");
}
else
{
printf("a >= 0");
}
return 0;
}
逻辑操作符
逻辑与&&
和逻辑或||
一定要区分好逻辑与&&
和按位与&
逻辑或`||`和按位或`|`
上面说过,按位与、按位或是按照两个数的每一个二进制位进行计算。
而逻辑与、逻辑或则是整个数进行操作。
逻辑与(并且)
比如3&&5=1
因为3不是0,5也不是0,所以表达式为真,返回1
3&&0=0
只要有一个表达式为假,那么整个值就为假。
逻辑或(或者)
表达式只要有一个为真,那么整个表达式就为真。
比如3||0=1
当然,在真正写代码的时候,逻辑与和或基本不用于判断两个数字的关系,都是用作判断两个表达式的关系。
比如我想判断a>1并且b<1时,执行…
就写a>1&&b<1
想判断a>1或者b<1时,执行…
就写a>1||b<1
逻辑与、逻辑或代码演示
#include<stdio.h>
int main()
{
int a = -1;
int b = -1;
if ((a > 0) && (b > 0))//&&是指“并且”
{
printf("a和b都是正数\n");
}
else if((a < 0) && (b < 0))
{
printf("a和b都是负数\n");
}
else
{
printf("a和b符号不同\n");
}
return 0;
}
短路问题
对于&&来说,左边只要为0 (false)后边就不计算了
对于||来说,左边只要为1(true),后边就不计算了
条件操作符
? :
这是一个三目操作符
表达式1?表达式2:表达式3
表达式1为真→执行表达式2
表示式1为假→执行表达式3
是不是有点像if语句?
条件操作符代码演示求两个数的较大值
// ? : 三目操作符
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("%d\n",( a > b ? a:b));//返回a和b中放入较大值
return 0;
}
逗号表达式
逗号表达式就是:
表达式1**,**表达式2**,**表达式3**,**......
从左向右计算 ,整个表达式结果是最后一个表达式的结果。
逗号表达式代码演示
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
//依次执行 先执行 a>b (因为a不大于b,此时返回值为0)
// 再执行 a = b + 10 (a赋值12,返回值为12)
// 最后执行 b = a + 1 (b赋值13, 返回值为13)
//逗号表达式取最后一个返回值13赋值给c
printf("%d\n", c);
return 0;
}
依次执行
先执行 a>b (因为a不大于b,此时返回值为0)
再执行 a = b + 10 (a赋值12,返回值为12)
最后执行 b = a + 1 (b赋值13, 返回值为13)
逗号表达式取最后一个返回值13赋值给c
逗号表达式的应用(去除冗余)
下表引用,函数调用和结构成员访问操作符
下表引用操作符 []
下标引用操作符的操作数是数组名和索引值
比如
创建一个数组 int arr[10];
arr[3] = 10
; arr和3 是操作数 索引数组中第4个元素(索引值从零开始)
函数调用 () 操作符
函数调用 () 操作符的操作数是函数名和参数
最少有一个操作数 也就是函数名
比如**printf("Hello World!\n");
**
printf和"Hello World!\n"是操作数,调用printf函数打印Hello World!
结构成员访问操作符 . 和 ->
. 结构体.成员名
-> 结构体指针->成员名(通过地址找到成员,在传址后用)
这两个的区别是
如果被操作的是结构体变量,用.
如果被操作的是结构体指针,用->
结构成员访问操作符 代码演示
我们定义两个函数,都用于给结构体赋值,一个传结构体(传值),一个传结构体地址(传址),看一下有什么区别。
#include <stdio.h>
#include <string.h>
struct Student
{
char name[10];
int age;
};
void SetStu1(struct Student stu)//传值函数 要用. 操作符
{
strcpy(stu.name,"张三");//对字符串赋值不能直接用=,要用strcpy函数
stu.age = 19;
}
void SetStu2(struct Student* stu)//传址函数 要用 -> 操作符
{
strcpy(stu->name, "李四");
stu->age = 20;
}
int main()
{
struct Student stu1 = {"xxxxx",0};//用于传值
struct Student stu2 = {"xxxxx",0 };//用于传址
SetStu1(stu1);//传值
SetStu2(&stu2);//传值
//猜一猜哪个赋值成功了?
printf("%s %d\n", stu1.name, stu1.age);
printf("%s %d\n", stu2.name, stu2.age);
return 0;
}
这个代码如果对strcpy函数不清楚可以看我之前发的有介绍:C语言常见的库函数的模拟实现(字符串、内存函数)
输出结果
传值调用很显然没有成功!这是因为形参是实参的临时拷贝,不会对实参有影响,传值调用是无法改变实参的!
本篇文章单纯说操作符如何使用,更对关于函数的细节欢迎点击: