今天我们就来总结总结我们C语言中的操作符吧,首先大家能想起来的操作符都有哪些呢?
我们大家可能都知道常见的算术运算符,关系运算符与逻辑运算符,但除了这些之外,还有一些用于完成特殊任务的运算符,比如位运算符。这些操作符虽然看起来很简单,大家很容易理解它的浅层意思,但它的用法特别多,一不小心我们就掉进坑里了,今天我就谈一下我对它的理解吧,如果有什么不正确的地方,也请大家指出来,我们可以互相讨论讨论。
算术操作符
我们知道的算术操作符有:“+”,“-”,“*”,“/”,“%”。
“+”,“-”和“/”都非常的简单,那我们就先来看看“%”操作符吧,在这里我们要注意的是,除了“%”操作符之外,其他几个操作符都可用于整数和浮点数,“%”操作符的两个操作数必须都为整数,返回的是整除之后的余数。“/”是当两个数都为整数的时候,为整数除法,只要其中有一个是浮点型,就是浮点型除法。
我们就来看一个“%”简单的例子吧:
#include<stdio.h>
int main()
{
int n = 6.0 % 3.0;
return 0;
}
很显然我们这个程序是编译不过去的,因为“%”的两个数必须是整型。
移位操作符
移位操作符其实又叫二进制移位操作符,是对一个数的二进制位(补码)进行左移右移,“<<”—左移操作符,“>>”—右移操作符。
左移eg:
include<stdio.h>
int main()
{
int n = 10;//00000000000000000000000000001010
int m = n<<1;
printf("%d\n",m);
printf("%d\n",n);
return 0;
}
代码的运行结果是:
m=20,n=10
从结果我们可以看出n其实并没有变,m是它左移一位之后的结果,我们可以总结出左移就是左边丢弃,右边补0,经过多组代码测试,我们可以发现向左移一位,有乘2的效果。
右移eg:
#include<stdio.h>
int main()
{
int n = 10;//00000000000000000000000000001010
int m = n>>1;
printf("%d\n",m);
printf("%d\n",n);
return 0;
}
运行结果是:
m=5,n=10
那同样的我们也可以得出一个结论:右移一位,有除2的效果。
右移又分为算术右移和逻辑右移,到底用的是哪一种右移方式呢,是由我们编译器决定的。
算术右移:右边丢弃,左边补原符号位。
逻辑右移:右边丢弃,左边补0。
这就是移位操作符,但不管怎么说,移位对被移位的数本身是不会产生影响的。
位操作符
位操作符有:& //按位与,| //按位或,^ //按位异或(两个二进制位相同为0,相异为1)
“&”eg:
include<stdio.h>
int main()
{
int n = -1;//10000000000000000000000000000001
//反码11111111111111111111111111111110
//补码11111111111111111111111111111111
int m = -3;//10000000000000000000000000000011
//反码11111111111111111111111111111100
//补码11111111111111111111111111111101
int k = n & m;//我们是用补码进行运算
//11111111111111111111111111111111
//&
//11111111111111111111111111111101
//=
//11111111111111111111111111111101(补码)--(-3)
printf("%d",k);
return 0;
}
运行结果是k=-3
“|”eg:
include<stdio.h>
int main()
{
int n = -1;//10000000000000000000000000000001
//反码11111111111111111111111111111110
//补码11111111111111111111111111111111
int m = -3;//10000000000000000000000000000011
//反码11111111111111111111111111111100
//补码11111111111111111111111111111101
int k = n | m;//我们是用补码进行运算
//11111111111111111111111111111111
//|
//11111111111111111111111111111111
//=
//11111111111111111111111111111111(补码)--(-1)
printf("%d",k);
return 0;
}
运行结果是:k=-1
“^”eg:
#include<stdio.h>
int main()
{
int a = 3;// 0011
int b = 10;//1010
^ //1001(9)
int k = a ^ b;
printf("%d\n",k);
return 0;
}
运行结果是:k=9
赋值操作符
”=“这是最简单的一个赋值符了,我们还有很多复合赋值操作符:+=,-=,*=,/=,%=,>>=,<<=,&=,|=,^=,这些都很简单,没什么可讲的。
单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
– 前置,后置–
++ 前置,后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
”!“ eg:
#include<stdio.h>
int main()
{
int n=10;
printf("%d",!n);
return 0;
}
我们这里的输出结果是:0,因为10是真,!10就是假,所以输出0。
”&“ eg:
#include<stdio.h>
int main()
{
int a = 1;
printf("%p\n", &a);
return 0;
}
我们可以看到这里我们输出的是a的地址。
”sizeof()“ eg:
int main()
{
short s = 3;
int a = 10;
printf("%d\n", sizeof(s = a + 3));//2
printf("%d\n", s);//3
return 0;
}
在这里我们看到的结果为什么回事2和3呢,因为sizeof()中的表达式不进行运算,所以s的值未被改变,sizeof()求得是与s的类型有关,与它的表达式无关。
sizeof和数组
eg:
#include<stdio.h>
void test1(int arr[10])
{
printf("%d\n", sizeof(arr));
}
void test2(char ch[10])
{
printf("%d\n", sizeof(ch));
}
int main()
{
int arr[10] = { 0 };
char ch[10] = { 0 };
printf("%d\n", sizeof(arr));//(1)
printf("%d\n", sizeof(ch));//(2)
test1(arr); //(3)
test2(ch); //(4)
return 0;
}
这里(1)是40,(2)是10,因为数组名放进sizeof()中代表的是计算整个数组的大小,而(3)和(4)的结果是4,因为它计算的是指针的大小,指针的大小是4个字节。
“~” eg:
int main()
{
int a = 0;
printf("%d\n", ~a);
return 0;
}
我们在这里得到的结果是-1,为什么会是-1呢,因为我们是对它的补码按位取反,得到的也是补码。
“++”和“–”都很简单,我们就简单的看一个例子吧:
int main()
{
int a = 1;
int b = a--;
printf("%d\n", a);//0
printf("%d\n", b);//1
return 0;
}
“*”eg:
#include<stdio.h>
int main()
{
int num = 10;
int* p = #
*p = 20;
printf("%d\n", num);
return 0;
}
这里输出的结果是20,因为*p就是num的值,改变*p就是间接改变num的值。
“(类型)”eg:
#include<stdio.h>
int main()
{
int num = (int)3.14;
printf("%d\n", num);
return 0;
}
这里的输出结果是3,3.14是浮点型,没办法赋值给整型,所以我们要对它强制类型转换,把它转成整型。
关系操作符
== 用于测试“相等”
<
<=
!= 用于测试“不相等”
>
大于等于
这些操作符都特别简单,没什么可说的,但我们要注意的是“==”这个操作符
eg:
x=get_value();
if(x=5)
//处理事务
这里我们看起来好像是x如果等于5就执行事务处理,实际上代码不是这个意思,如果我们将判断的==搞成了赋值的=,那么这个事务无论x是多少都会执行了。
警告:
如果在编程过程中,我们不小心将==写出=,造成的后果是非常严重的。
逻辑操作符
&&—–逻辑与
|| —–逻辑或
“&&”eg:
#include<stdio.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("%d %d %d %d\n", a, b, c, d);
return 0;
}
这里的输出结果是:1 2 3 4,这是为什么呢?
因为我们这里的a是后置++,先使用再自加,a是0,所以&&后边的表达式直接就不用计算了,b,c,d的值没有改变,然后a再进行自加。
如果刚开始我们让a=1,输出的结果就是:2 3 3 5
因为a为1,是真,所以我们要进行后边的表达式,a自加变成2,然后b也要进行自加,a++&&++b为真就相当于1,因为是逻辑与,所以后边也要进行运算,d进行自加。
“||”eg:
#include<stdio.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("%d %d %d %d\n", a, b, c, d);
return 0;
}
我们这里的运行结果是:1 3 3 4
因为是逻辑或,所以a为假时,后边的表达式要进行计算,b自加,a在使用之后要自加,前边为真了,所以后边的d++不用进行计算了。
条件操作符
exp1 ? exp2 : exp3
eg:
#include<stdio.h>
int main()
{
int a = 0;
int b = 3;
int max = a > b ? a : b;
printf("%d\n", max);
return 0;
}
这里的输出的结果是3,max = a > b ? a : b的意思是如果a大于b,把a赋给max,如果a>b不成立,则把b赋给max。
逗号表达式
exp1,exp2,exp3,……,expN
逗号表达式就是用逗号隔开的多个表达式,从左向右依次进行,整个表达式的结果是最后一个表达式的结果。
eg:
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);
printf("%d\n", c);
return 0;
}
运行结果是:13
下标引用,函数调用和结构成员
下标引用操作符:[],有两个操作数
eg:
#include<stdio.h>
int main()
{
int i = 0;
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
函数调用操作符:(),至少接收一个操作数
eg:
#include<stdio.h>
void test()
{
printf("哈哈\n");
}
void print(int n)
{
printf("%d\n", n);
}
int main()
{
int n = 0;
test();
print(3);
return 0;
}
访问一个结构的成员操作符:.结构体.成员名 ; ->结构体指针->成员名
eg:
#include<stdio.h>
#include<string>
struct Stu
{
char name[20];
int age;
char sex[5];
char tele[12];
};
int main()
{
struct Stu s;
strcpy(s.name, "lisi");
s.age = 20;
strcpy(s.sex, "男");
printf("%s\n", s.name);
printf("%d\n", s.age);
printf("%s\n", s.sex);
return 0;
}
#include<stdio.h>
#include<string>
struct Stu
{
char name[20];
int age;
char sex[5];
char tele[12];
};
int main()
{
struct Stu s;
struct Stu* ps = &s;
strcpy((*ps).name, "zhangsan");
ps->age = 20;
strcpy((*ps).sex, "男");
printf("%s\n", s.name);
printf("%d\n", s.age);
printf("%s\n", s.sex);
return 0;
}
当我们定义了一个结构体的指针时,我们可以用箭头来找这个结构体成员
当给一个结构体变量时,我们用.来找结构体成员
好了,今天呢,我们就把我们的所以的操作符都总结完了。