目录
前言
在C语言中,操作符的分类可以说是五花八门,但对于我们日常写代码时又非常重要。
下面,一起来认识操作符的分类:
1算术操作符
2移位操作符
3位操作符
4赋值操作符
5单目操作符
6关系操作符
7逻辑操作符
8条件操作符
9逗号表达式
10下标引用、函数调用和结构成员
一算术操作符
+ - * / %(取余数)
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法,而只要有浮点数执行的就是浮点数除法.
3. % 操作符的两个操作数必须为整数.
二移位操作符
<< 左移位操作符 >>右移位操作符
注:移位操作符的操作数只能是整数
2.1左移位操作符
规则:左边抛弃,右边补0
2.2右移位操作符
它分2种情况:算术右移与逻辑右移
算术右移:左边用0填充,右边丢弃
逻辑右移:左边用原该值的符号位填充,右边丢弃
在VS中,选择的时逻辑右移,如下:
三位操作符
& 按位与 | 按位或 ^按位异或
注:他们的操作数也必须是整数
3.1变态的笔试题
不能创建临时变量(第三个变量),实现两个数的交换。
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a^b;
b = a^b;//b=a^b^b
a = a^b;//a=a^b^a
printf("a = %d b = %d\n", a, b);
return 0;
}
在平常中写代码中,我们要写出的代码要不仅逻辑清晰,看代码也要清晰。上面这种仅仅是出现在笔试题中,平常尽量避免以上这种代码。
3.2练习
编写代码实现:求一个整数存储在内存中的二进制中1的个数。
int print1(unsigned int n)//这种思路接收的参数必须时无符号的整数,避免传入-1计算错误
{
int count = 0;
while (n)
{
if(n % 2==1)
count++;
n /= 2;
}
return count;
}
int print2(int n)
{
int count = 0;
for (int i = 0; i < 32; i++)
{
if ((n >> i)&1 == 1)
count++;
}
return count;
}
int print3(int n)//妙解
{
int count = 0;
while (n)
{
n=n& (n - 1);//带入具体数据更好理解
count++;
}
return count;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d", print3(n));
return 0;
}
四赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值。
4.1复合操作符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
举例:
int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。使得更加简便
五单目操作符
5.1单目操作符介绍
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置--(先--,在使用)、后置--(先使用,在--)
++ 前置++(先++,在使用)、后置++(先使用,在++)
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
#include <stdio.h>
int main()
{
int a = -10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof a);//这样写行不行?
printf("%d\n", sizeof int);//这样写行不行?
return 0;
}
sieof在使用的时候,后面可以直接加变量名(不能时类型)来使用,说明sizeof是一个操作符。我们在日常使用时通常加括号来使用,会误以为它是函数,要注意。
六关系操作符
=
>=
<=
!= (有来测试不相等)
== (用来测试相等)
注意:在写表达式时,经常会把==写成=导致程序的崩溃。
解决之一:把变量名写在之后.如if(a==10)写成if(10==a)即使漏写一个=,编译器直接报错.(好修改)
七逻辑操作符
&& 逻辑与
|| 逻辑或
区分逻辑操作符与算术操作符:
1&2----->0(计算的结果)
1&&0---->0(结果为假)
1|2----->3(计算的结果)
1||0---->1(结果为真)
7.1笔试题
#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;//结果a=1 b=2 c=3 d=4
//i = a++||++b||d++;//结果a=1 b=3 c=3 d=4
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
//程序输出的结果是什么?
解决上面的问题,要注意的是:逻辑操作符会从左向右依次执行,&&只要一个为假就结束,||只要有一个为真就结束。
八条件操作符
exp1(表达式) ? exp2(表达式为真执行) : exp3(表达式为假执行)
8.1练习
if (a > 5)
b = 3;
else
b = -3;
转换成条件表达式,是什么样?
a > 5 ? b = 3 : b = -3;//一句搞定
在通常写代码时,只有if else 这些简单的判断句时就可以使用它来达到简便。
九逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//c=13
//代码2
if (a =b + 1, d=a / 2, d > 0)//最后的表达式才会起作用,但前面的表达式执行可能会对最后的结果产生影响
//代码3
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
a = get_val();
count_val(a);
}
如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}
十下标引用,函数调用和结构体成员操作符
10.1下标引用操作符[]
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
[ ]的两个操作数是arr和9。
10.2函数调用操作符()
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
但函数调用操作符也可以什么都不传。
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //实用()作为函数调用操作符。
test2("hello bit.");//实用()作为函数调用操作符。
return 0;
}
10.3结构体成员操作符
. 使用,结构体.成员名
-> 使用,结构体指针->成员名
#include <stdio.h>
struct Stu
{
char name[10];
int age;
char sex[5];
double score;
};
void set_age1(struct Stu stu)
{
stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
pStu->age = 18;//结构成员访问
}
int main()
{
struct Stu stu;
set_age1(stu);
set_age2(pStu);
return 0;
}
十一表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
有些表达式的操作数在求值的过程中可能需要转换为其他类型,一般是转化为高字节类型计算。
11.1隐式类型转换
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
例子:
int main()
{
char a = 3; //char a=00000011<-存储在char类型中a=00000000000000000000000000000011
char b = 127;//cahr b=01010111<-存储在char类型中b=00000000000000000000000001111111
char c = a + b; //char a与char b相加发生整形提升来进行计算
//计算结果:00000000000000000000000010000010存储在char类型中
//char c=10000010
printf("%d\n", c);//以整形形式打印c
//进行整形提升,高位补符号位->11111111111111111111111110000010->是高位是1,是负数,结果转化为源码
// ->11111111111111111111111110000001
// ->10000000000000000000000001111110->-126
return 0;
}
系统内部真的有整形提升吗?
//实例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;
}
如果有,实例1中的a,b要进行整形提升,但是c不需要整形提升 a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真,打印c。
11.2算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行。(一般低类型转换为高类型计算)
long double > double > float > unsigned long > int > long int > unsigned int > int
但算术转换也要合理,不然会有精度丢失!
float f = 3.14;
int num = f;//隐式转换,会有精度丢失
11.3操作符的属性
复杂表达式求值有以下几个因素:
第一考虑优先级
如果优先级相同则考虑结合性
控制求值顺序只有特殊的三个(逻辑与,逻辑或,条件操作符)
11.4问题表达式
由于操作符的属性由多方面元素影响,由此会产生问题表达式,大都是编译器对于操作符的顺序各有各的"见解",造成计算结果的不同.
//表达式1
a*b + c*d + e*f
第一种:
第二种:
你或许会说:上面两种方法计算出来的不也一样的吗?那你有没有想过:如果前面先*完后对后面的变量有影响呢?(++a)
//表达式2
c + --c;
像这个就很清楚的展现出问题来了,想进行--c势必对前者的c产生影响.
//表达式3
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
在不同的编译器上的不同结果:
//表达式4
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf( "%d\n", answer);//输出多少?
return 0;
}
这个代码有没有问题?
乍一看,没问题.实际上,有问题.
尽管在其它编译器上的结果都相同,但问题是:编译器要先调用那个我们并不清楚...
函数的调用先后顺序无法通过操作符的优先级确定
//表达式5
#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
return 0;
}
在VS中,它认为的计算顺序是这样的: 结果是:12=4+4+4
但别的编译器不认为,在Linux环境编译的结果:10=3+3+4 ,它所认为的顺序:
原因:这段代码中的第一个 + 在执行的时候,第三个++是否执行,这个是不确定的,单依靠操作符的优先级和结合性是无法决定第一个 + 和第三个前置 ++ 的先后顺序.所以有不同的结果.
总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的.
相关练习
写代码,打印整数二进制的奇数位和偶数位
i=0移到第一个bit位
int main()
{
int n = 0;
scanf("%d", &n);
printf("奇数位:");
for (int i = 30; i >= 0; i -= 2)
{
int ret = (n >> i) & 1;
printf("%d ", ret);
}
printf("\n偶数位:");
for (int i = 31; i >= 1; i -= 2)
{
int ret = (n >> i) & 1;
printf("%d ", ret);
}
return 0;
}
#include <stdio.h>
int i;//0
int main()
{
i--;
//i是int类型,要转化成无符号整形 sizeof(i)计算的结果是size_t是无符号整形
if (i > sizeof(i))
{
printf(">\n");
}
else
{
printf("<\n");
}
return 0;
}
//打印的结果:>
#include <stdio.h>
int main() {
int n=0,m=0;
scanf("%d%d",&n,&m);
int arr1[1000]={0};
int arr2[1000]={0};
int arr[3000]={0};
int i=0,j=0;
for(i=0;i<n;i++)
{
scanf("%d",&arr1[i]);
}
for( j=0;j<m;j++)
{
scanf("%d",&arr2[j]);
}
i=0,j=0;
int k=0;
while(i<n&&j<m)
{
if(arr1[i]<arr2[j])
{
arr[k]=arr1[i];
i++;
k++;
}
else {
arr[k]=arr2[j];
j++;
k++;
}
}
if(i==n)
{
while(j<m)
{
arr[k++]=arr2[j++];
}
}
else {
while(i<n)
arr[k++]=arr1[i++];
}
for(int i=0;i<n+m;i++)
{
printf("%d ",arr[i]);
}
}
#include <stdio.h>
int main() {
int y, m;
while (scanf("%d %d", &y, &m) != EOF) {
int days[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
if(y%4==0&&y%100!=0||y%400==0)
{
days[2]++;
}
printf("%d\n",days[m]);
}
return 0;
}
#include <stdio.h>
// n/10 * n%10+n/100 * n%100+n/1000 * n%1000+n/10000 * n%10000
int main() {
for(int i=10000;i<100000;i++)
{
int sum=0;
for(int j=10;j<=10000;j*=10)
{
sum+=(i/j)*(i%j);
}
if(sum==i)
{
printf("%d ",i);
}
}
return 0;
}
最后
感谢你的观看,能三连支持一波就更好了。