前言
C语言中操作符总共有10种,分别是:
算术操作符,移位操作符,位操作符,赋值操作符,单目操作符,关系操作符,逻辑操作符,条件操作符,逗号表达式,下标引用、函数调用和结构成员。
这里我挑了一些重点和易错点来说!
算术操作符,移位操作符,位操作符,复合运行算符,单目操作符、条件操作符,逗号操作符,下标引用、函数调用和结构成员
Let’s get it!
算术操作符
分为:
加:+
减:-
乘:*
除:/
取余:%
注意:
1、/ 如果是整型除法,其结果是商
这里请注意,我说的是商!
如 a ÷ b = c …d,c叫做商,d叫做余数!例子: 9 / 4 = 2
如果两边有一个小数,则进行的就是小数除法,例子: 9 / 4.0 = 2.250000
2、% 代表求模运算,即余数,a % b = c,其中c的范围在 [0,b)
移位操作符
分为:
左移操作符 :<<
右移操作符:>>
在讲解移位操作符以前,这里要强调一个概念:原码反码补码
原反补
学了你就可以知道为char a=127, char b; b=a+1 这个时候你为什么b=-128
一个数在计算机内部如果是有符号数,则其最高位作为符号位,如果符号位为0,表示该数为正数,如果符号位为1,表示该数为负数。(0正1负)
以下求原反补的过程:
原码:最高位作为符号数,其余各位为数值位(0正1负)
反码:正数的反码与原码相同,负数的反码是在原码的基础上符号位不变,其余各位按位取反
补码:正数的补码与原码相同,负数的补码是在反码的基础上加1
这里看例题:
例:求+25和-25的原反补
①不考虑正负,将25转换成二进制
25D=11001B
② +25 -25
原: 00011001 10011001
反: 00011001 11100110
补: 00011001 11100111
例:求+30和-30的原反补
①30D=11110B
② +30 -30
原: 00011110 10011110
反: 00011110 11100001
补: 00011110 11100010
计算机中用的是补码,什么是补码,怎么去理解补码?
补码可以理解成一个循环。
8位二进制表示的有符号数范围是 -128~127
如果还有不懂的可以去百度一下!
这里我们回到移位操作符上
在计算机内存中,所操作的一切都是操作的 补码,补码,补码,补码!!!
重要的事情多说几遍,只是显示的时候才把补码还原回原码,然后转换成十进制
移位规则:
<< :左移操作符:不论算术移位还是逻辑移位,均将左边的数舍弃,右边空缺位补0(左边丢弃,右边补0)
>> :右移操作符:当进行逻辑移位时,右边位丢弃,左边空缺位补0(右边丢弃,左边补0) 再次强调,计算机操作的是补码
总结:
右移一位具有 /2效果; 右移2位 具有 /4 效果;右移三位 具有 /8 效果;(右移效果只针对正数)…
左移一位 具有x2效果;左移两位 具有x4效果;左移三位 具有x8效果;(左移效果针对所有数)…
注意:
-
移位操作不改变原值 -
移位时不能移负数位
位操作符
符号:
& :按位与 (两个都是1才是1,否则0)
110101
101110
上面两个结果为 : 100100
| :按位或 (只要有1就是1)
1011011
0101100
上面两个结果为: 1111111
^ :按位异或 (相同为0,相异为1)
1010001
1010101
上面两个结果为:0000100
看到这里可能有点懵,所以我出两道关于位操作符的习题
1、数字交换
给出两个数字 a 和 b ,要求除了这两个变量以外,不准创建任何变量,达到ab交换
#include <stdio.h>
int main()
{
int a,b;
scanf("%d%d",&a,&b);
a = a^b;
b = a^b;
a = a^b;
printf("%d %d", a, b);
return 0;
}
是不是感觉没看懂? 我们以 a = 3,b = 5为例;
假设:a ^ b = m ,那么我们反过来推导,m ^ a = b 或者 m^b = a
所以这段代码
a = a^b;
b = a^b;
a = a^b;
为了更好理解我暂时用am代替 a^b 出来的值a(图中m)
am = a ^ b
b = am ^ b (新得到的值其实是a,只是把值赋给了b)
a = am ^ b (am异或b,其实是异或的a,能理解吧?那么异或a得到的值就是b,所以赋值给a)
所以这样就是不要变量达到了完美的交换!
再问:数字的二进制(补码)有多少个1 ???
我们先不管二进制,假设给你一个数字a = 12334565,喊你求这个数字有几个3?
正常思路是不是 a%10 == 3 a = a/10 --> a%10 == 3 a = a/10 --> a%10 == 3 a = a/10 这样循环下去计算?
那么我们二进制也是一样 a%2 == 1 a = a/2 这样循环下去数
代码一:
#include <stdio.h>
int main()
{
int n = 15;
int count = 0;
while (n)
{
if (n % 2 == 1)
count++;
n /= 2;
}
printf("%d", count);
return 0;
}
但是这样写只能算正数,所以有缺陷
下面我就介绍 位操作
我们知道只有数字1 & 1 结果才是1
步骤一: 所以我们可以通过把一个数字的二进制与00000000000000000000000000000001 相&
步骤二: 如果结果为1,代表末尾为1
步骤三: 然后把该数字右移一位,再 与 000000000000000000000000000000001 相&
以此循环…
代码二:
#include <stdio.h>
int main()
{
int n = -1;
int count = 0;
for(int i = 0;i<32;i++)
{
if (n & 1 == 1)
count++;
n = n >> 1;
}
printf("%d", count);
return 0;
}
复合运算符
这个没什么讲的 ,就是组合起来操作
例子:
a = a + 3 <==> a += 3
a = a & 2 <==> a &= 2
a = a >> 1 <==>a >>= 1
单目操作符
字面理解:目,眼睛;即这个操作符只需要一个操作数
例如:!3、-4、&a、a++
这里主要讲解一下 ++ – ~ sizeof (类型)
其中sizeof() 和strlen() 的区别请看这里:sizeof和strlen的区别
例1:sizeof
#include <stdio.h>
int main()
{
short s = 0;
int a = 10;
printf("%d\n", sizeof(s = a+5));
printf("%d\n", s);
return 0;
}
结果是多少???
2 0
因为sizeof里面的表达式中s不参与运算
例2:++
1、前置++:即先++,再使用 代码如下:
#include <stdio.h>
int main()
{
int a = 5;
printf("%d ", a++);
printf("%d", a);
return 0;
}
5 6
2、后置++:即先使用,再++
代码如下:
#include <stdio.h>
int main()
{
int a = 5;
printf("%d ", ++a);
printf("%d", a);
return 0;
}
6 6
-- 的使用和++ 一样
例3:~
~ :按位取反
问题: 假设我想把 00001011 的倒数第三个0改为1 怎么用代码弄?
很简单,我们只有把它和00000100 相 | 一下就行,那么00000100怎么来的??
数字1左移两位 1<<2
代码如下:
#include <stdio.h>
int main()
{
int a = 11;
int ret;
ret = a | (1<<2);
printf("%d", ret);
return 0;
}
1111刚好就是15
再问: 那如果我想把1111改回去呢???
-
00000000 00000000 0000000 00001111 -
我们需要把它和11111111 11111111 11111111 111110111 相& -
那11111111 11111111 11111111 111110111怎么得来的呢??? -
00000000 00000000 00000000 00000100取反得来 -
00000000 00000000 00000000 00000100而这个又是1左移两位 -
00000000 00000000 00000000 00000001(数字1)
所以逻辑是:首先 1<<2,然后取反,最后相与
代码如下:
#include <stdio.h>
int main()
{
int a = 15;
int ret;
ret = a & (~(1<<2));
printf("%d", ret);
return 0;
}
例4:(类型)(强制类型转换)
srand((unsigned int)time(NULL));
注意:
1.sizeof 是一个操作符,关键字,而不是函数,求的是操作数的类型长度(以字节为单位)
2.sizeof 求类型的长度时不可省略括号,求变量的长度时可以省略括号
3.! 操作符是对一个数做逻辑反操作,而~操作符是对一个数的二进制按位取反
4.前置++、前置–是先使用,再++;而后置++、后置–是先–,再使用
条件操作符
符号:exp1 ? exp2 : exp3
含义:表达式exp1如果成立,则返回表达式2的值,否则返回表达式3的值
例子: 例如我们需要把 a,b中的大值给max,小值给min
#include <stdio.h>
int main()
{
int a = 15,b = 24;
int max,min;
max = a>b?a:b;
min = a>b?b:a;
printf("max = %d min = %d", max,min);
return 0;
}
逗号表达式
符号:exp1, exp2, exp3, ..., expN
含义:逗号表达式(用逗号隔开的表达式),从左往右依次执行;整个表达式的结果为最后一个表达式的结果
举例说明:
int a = 3,b = 4;
c = (a>b,a = b*4,b = a+2)
c等于多少呢? 答案:18
代码如下:
#include <stdio.h>
int main()
{
a = get_val();
count(a);
while(a>0)
{
a = get_val();
count(a);
}
return 0;
}
#include <stdio.h>
int main()
{
while(a = get_val(),a>0,count(a))
{
}
return 0;
}
注意:
逗号表达式的结果虽然是最后一个表达式的结果,但不可认为与前面的表达式就无关了,
因为前面表达式可能会影响最后一个表达式的结果
下标引用、函数调用和结构成员
1、下标引用
符号:[ ]
符号说明:下标引用操作符,有两个操作数(数组名和索引值)
举例说明:
int arr[10] = {3,7,4,8,2,15,25,6,9,1};
arr[3] = 8;
注意:下标引用共有两个操作数(数组名和索引值)
2、函数调用
符号:()
符号说明:函数调用操作符,有一个或多个操作数(函数名和参数)。
举例说明:
void test1()
{
printf("fine day!\n");
}
void test2(char *ch)
{
printf("%s\n",ch);
}
int main()
{
test1();
test2("fine day!");
}
注意:函数调用操作符有一个或多个操作数
3、访问结构体成员
符号:.
符号说明:结构体对象
举例说明:
struct student
{
char name[10];
int age;
char sex[10];
};
#include <stdio.h>
int main()
{
struct student xiaoming = { "小明",25,"男人" };
printf("姓名:%s\n", xiaoming.name);
printf("年龄:%d\n", xiaoming.age);
printf("性别:%s\n", xiaoming.sex);
return 0;
}
4、结构体指针访问
符号:->
符号说明:结构体指针
举例说明:
struct student
{
char name[10];
int age;
char sex[10];
};
#include <stdio.h>
int main()
{
struct student xiaoming = { "小明",25,"男人" };
struct student* people = &xiaoming;
printf("姓名:%s\n", people->name);
printf("年龄:%d\n", people->age);
printf("性别:%s\n", people->sex);
return 0;
}
注意:当结构体中有数组成员时,给该成员赋值用strcpy()函数,将目标串拷贝给该数组成员
see you!
|