目录
算数操作符
包括 : + - * % /
1.他们都是双目操作符,即都有左操作数和右操作数
2.%操作符左右操作数必须是整数,其他的整数和浮点数都可以
3./ 操作符 : 若左右操作数都是整数,执行整数除法;左右若有一个浮点数,则执行的是浮点数的计算。
1>
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
float c = 0;
scanf("%d %d %f", &a, &b, &c);
printf("%d %f", a / b, a / c);
return 0;
}
2>%不能有浮点数操作数
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
float c = 0;
scanf("%d %d %f", &a, &b, &c);
printf("%d %f", a % b, a % c);
return 0;
}
移位操作符
注:不能移动负数位,未定义
>> 右移操作符
<< 左移操作符
1)左移操作符
移位规则:二进制序列向左移动几位,右边补0;将二进制左边的几位去掉,右边在补上相同数量的0;
#include <stdio.h>
int main()
{
int a = 3; //00000000 00000000 00000000 00000011
//一个数在内存中以补码的形式存在,正数的源码,反码,补码都相同;负数的源码按位取反得反码,反码加一得补码
//0000000 00000000 00000000 000000110 a<<1 1 * 2^2 + 1 * 2 ^1 = 6
//10000000 00000000 00000000 00000011 b 的源码
//11111111 11111111 11111111 11111100 b 的反码
//11111111 11111111 11111111 11111101 b 的补码
//一个数是以源码的形式展现
//11111111 11111111 11111111 11111101 b 的补码
//11111111 11111111 11111111 11111010 b<<1 的补码
//11111111 11111111 11111111 11111001 b<<1 的反码
//10000000 00000000 00000000 00000110 b<<1 的源码 -1 (1 * 2 ^ 2 + 1 * 2 ^ 1 ) = -6
int b = -3;//10000000 00000000 00000000 00000011
printf("%d %d", a << 1, b << 1);
return 0;
}
00000000 00000000 00000000 00000011 a的源码
一个数在内存中以补码的形式存在,正数的源码,反码,补码都相同;负数的源码按位取反得反码,反码加一得补码
0000000 00000000 00000000 000000110 a<<1 1 * 2^2 + 1 * 2 ^1 = 6
10000000 00000000 00000000 00000011 b 的源码
11111111 11111111 11111111 11111100 b 的反码
11111111 11111111 11111111 11111101 b 的补码
一个数是以源码的形式展现
11111111 11111111 11111111 11111101 b 的补码
11111111 11111111 11111111 11111010 b<<1 的补码
11111111 11111111 11111111 11111001 b<<1 的的补码
10000000 00000000 00000000 00000110 b<<1 的源码 -1 (1 * 2 ^ 2 + 1 * 2 ^ 1 ) = -6
移位操作在没有赋值时,原值并没有改变
#include <stdio.h>
int main()
{
int a = 3;
int b = -3;
printf("%d %d\n", a << 1, b << 1);
printf("%d %d\n", a, b);//移位操作在没有赋值时,原值并没有改变
return 0;
}
2)右移操作符
移位规则:逻辑移位和算术移位
逻辑移位:去掉右边,左边用0补充
算数移位:去掉右边,左边用符号位补充
#include <stdio.h>
int main()
{
int a = 3;
int b = -3;
//a源码 : 00000000 00000000 00000000 00000011
//a右移1 : 00000000 00000000 00000000 00000001 a>>1 = 1
//b源码 : 10000000 00000000 00000000 00000011
//b反码 : 11111111 11111111 11111111 11111100
//b补码 : 11111111 11111111 11111111 11111101
//逻辑移 :
// 01111111 11111111 11111111 11111110
//反码 : 01111111 11111111 11111111 11111101
//源码 00000000 00000000 00000000 00000010 b = 2
//算数移:
// 111111111 11111111 11111111 1111110
// 111111111 11111111 11111111 1111101
// 100000000 00000000 00000000 0000010 b = -2
printf("%d %d\n", a >> 1, b >> 1);
return 0;
}
可知,在vs中,是算数右移
位操作符
& 按位与 有0为0,同1为1
| 按位或 有1为1,同0为0
^ 按位异或 相同为0,不同为1
#include <stdio.h>
int main()
{
int a = 1;//00000000 00000000 00000000 00000001
int b = 5;//00000000 00000000 00000000 00000101
printf("%d\n", a & b);//00000000 00000000 00000000 00000001 a&b = 1
printf("%d\n", a | b);//00000000 00000000 00000000 00000101 a|b = 5
printf("%d\n", a ^ b);//00000000 00000000 00000000 00000100 a^b = 4
return 0;
}
#include <stdio.h>
int main()
{
int a = 3; //00000000 00000000 00000000 00000011 a 的源码
int b = -10; //10000000 00000000 00000000 00001010 b 的源码
//11111111 11111111 11111111 11110101 b 的反码
//11111111 11111111 11111111 11110110 b 的补码
printf("%d\n", a & b);//00000000 00000000 00000000 00000010 a&b = 2
printf("%d\n", a | b);//11111111 11111111 11111111 11110111 a|b 的补码
//11111111 11111111 11111111 11110110 a|b 的反码
//10000000 00000000 00000000 00001001 a|b 的源码 a|b = -9
printf("%d\n", a ^ b);
//00000000 00000000 00000000 00000011 a 的源码
//11111111 11111111 11111111 11110110 b 的补码
//11111111 11111111 11111111 11110101 a^b 的补码
//11111111 11111111 11111111 11110100 a^b 的反码
//10000000 00000000 00000000 00001011 a^b 的源码 a^b = -11
return 0;
}
1.不创建变量,完成两个数的交换
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("a = %d b = %d\n", a, b);
//一个数^ 它本身 = 0;
//0 ^ 一个数 = 这个数
a = a ^ b;
b = a ^ b;//b = a ^ b = a ^ b ^ b = a ^ 0 = a
a = a ^ b;//a = a ^ b = a ^ a ^ b = 0 ^ b = b
printf("a = %d b = %d\n", a, b);
return 0;
}
赋值操作符
复合赋值符:
+= -= *= /= %= >>= <<= &= |= ^=
#include <stdio.h>
int main()
{
int a = 5;
printf("%d\n", a += 1); //a = a + 1 5+1 = 6
printf("%d\n", a -= 1); //a = a - 1 6-1 = 5
printf("%d\n", a *= 1); //a = a * 1 5*1 = 5
printf("%d\n", a /= 1); //a = a / 1 5/1 = 5
printf("%d\n", a >>= 1);//a = a >>1 a>>1= 2
printf("%d\n", a <<= 1);//a = a <<1 a<<1= 4
printf("%d\n", a %= 1); //a = a % 1 a%1 = 0
printf("%d\n", a &= 1); //a = a & 1 a&1 = 0
printf("%d\n", a |= 1); //a = a | 1 a|1 = 1
printf("%d\n", a ^= 1); //a = a ^ 1 a^1 = 0
return 0;
}
单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置减减
++ 前置、后置加加
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
#include <stdio.h>
int main()
{
int a = 6;//!0为假,非0为真;
int b = 0;//!真 = 0; !假 = 1。
int* pa = &a;
printf("%d\n", !a);
printf("%d\n", !b);
printf("%p\n", pa);
printf("%p\n", &a);
printf("%d\n", sizeof(pa));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
//printf("%d\n", sizeof int);erro错误
printf("%d\n", sizeof a);
printf("%d\n", ~a);
printf("%d\n", ++a);//前置++,先++后使用;
printf("%d\n", a++);//后置++,先使用后++
printf("%d\n", --a);//前置--,先--后使用
printf("%d\n", a--);//先使用,后--
printf("%d\n", *pa);
float d = (float)a;//a的类型是int 前面(float) 把 int 强制转换成 float 类型
printf("%f\n", d);
return 0;
}
sizeof 和数组
#include <stdio.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
char arr2[10] = { 'a','b','c','d','e','f','g','h','i','j' };
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr2));
printf("%p\n", arr1);//数组名表示首元素的地址
printf("%p\n", arr1 + 1);//数组名+1 表示跳过一个元素的地址
printf("%p\n", &arr1);
printf("%p\n", &arr1 + 1);//取地址数组名+1,跳过整个数组
printf("%p\n", &arr1[0]);//第一个元素的地址
printf("%p\n", &arr1[0] + 1);//跳过一个元素的地址
return 0;
}
关系操作符
> 大于
>= 大于等于
< 小于
<= 小于等于
!= 不等于
== 等于
#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
if (a > 0)
printf("大于0\n");
if (a < 0)
printf("小于0\n");
if (a >= 0)
printf("大于等于0\n");
if (a <= 0)
printf("小于等于0\n");
if (a==0)
printf("等于0\n");
if(a != 0)
printf("不等于0\n");
return 0;
}
逻辑操作符
&& 逻辑与;同时满足时为真;只要左边有一个假,就不进行计算
|| 逻辑或;只要满足一个就为真;只要左边有一个正,就不进行计算
#include <stdio.h>
int main()
{
int i = 0;
int a = 0;
int b = 2;
int c = 3;
int d = 4;
i = a++ && ++b && d++;//a++是先使用,后++;a为0,为假,则bd不算
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//a 1; b 2;c 3;d 4
return 0;
}
#include <stdio.h>
int main()
{
int i = 0;
int a = 1;
int b = 2;
int c = 3;
int d = 4;
i = a++ && ++b && d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//a 2; b 3;c 3;d 5
return 0;
}
#include <stdio.h>
int main()
{
int i = 0;
int a = 0;
int b = 2;
int c = 3;
int d = 4;
i = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//a 1; b 3;c 3;d 4
return 0;
}
#include <stdio.h>
int main()
{
int i = 0;
int a = 1;
int b = 2;
int c = 3;
int d = 4;
i = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//a 2; b 2;c 3;d 4
return 0;
}
#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
if (a > 0 && a <= 18)
printf("未成年\n");
if (a < 0 || a>18)
printf("成年\n");
return 0;
}
#include <stdio.h>
int main()
{
int a = 0;
while ((scanf("%d", &a)) != EOF)
{
if (a > 0 && a <= 18)
printf("未成年\n");
if (a < 0 || a>18)
printf("成年\n");
}
return 0;
}
条件表达式
格式:表达式1? 表达式2 : 表达式3(三目操作符)
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("%d\n", a > b ? a : b);
return 0;
}
逗号表达式
逗号表达式,就是用逗号隔开的表达式;逗号表达式的结果,就是最后一个表达式的结果;
格式: 表达式1,表达式2,表达式3,......,表达式n
#include <stdio.h>
int main()
{
int a = 0;
int b = 3;
int c = 4;
int d = (a = a + 3, b = b + a, c + b);
printf("%d", d);
return 0;
}
下标引用、函数调用和结构成员
下标引用操作符: []
函数调用操作符: ()
结构成员操作符: * 或 ->
下标应用操作符:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
for (int i = 0; i < 10; i += 2)
{
printf("arr[%d] = %d\n", i, arr[i]);
}
return 0;
}
函数调用:
#include <stdio.h>
void menu()//定义菜单函数
{
printf("***********************\n");
printf("***********************\n");
printf("***********************\n");
printf("***********************\n");
}
int Add(int x, int y)//定义加法函数
{
return x + y;
}
int my_strlen(char *s)//定义字符串函数
{
if (*s == '\0')
{
return 0;
}
else
return 1 + my_strlen(s + 1);
}
int main()
{
int a = 0;
int b = 0;
char arr[20] = { 0 };
scanf("%s", arr);
menu();//调用打印菜单函数
printf("输入两个数:>");
scanf("%d %d", &a, &b);
printf("%d\n", Add(a, b));//打印加法函数的结果
printf("%d\n", my_strlen(arr));//打印求字符串函数的结果
return 0;
}
结构成员操作符:
#include <stdio.h>
struct stu
{
char name[10];
int age;
char sex[10];
double score;
};
int main()
{
struct stu s = { "zhangsan",20,"male",90.0 };
printf("%s %d %s %.2lf\n", s.name, s.age, s.sex, s.score);//结构体变量.结构体成员
struct stu* ps = &s;
printf("%s %d %s %.2lf\n", (*ps).name, (*ps).age, (*ps).sex, (*ps).score);//*ps == s
printf("%s %d %s %.2lf\n", ps->name, ps->age, ps->sex, ps->score);//结构体指针.结构体成员
return 0;
}
表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定的;有些表达式的操作数在求值时会发生类型转换。
隐式类型转换:c的整型运算至少以整型的精度来进行。
为了获得精度,char 和 short 类型 在运算之前被转换成整型类型,称为整型提升。(整型提升时,高位补充符号位)
1)char的整型提升:
#include <stdio.h>
int main()
{
char a = 5;
char b = 126;
char c = a + b;
//char a 00000101
//char b 01111110
//a和b在计算时,发生了整型提升(计算时,是以补码计算的)
//a :00000000 00000000 00000000 00000101
//b :00000000 00000000 00000000 01111110
//c :00000000 00000000 00000000 10000011
//对外显示时,显示的是源码
//c :10000011(在char中存储)
//c :11111111 11111111 11111111 10000011 -补码
//c :11111111 11111111 11111111 10000010 -反码
//c :10000000 00000000 00000000 01111101 -源码
//c = -1(2^6 + 2^5 + 2^4 + 2^3 +2^2 + 2^0)=-(1 + 4 +8 +16 +32 + 64) = -125
printf("%d", c);
return 0;
}
2)short的整型提升
#include <stdio.h>
int main()
{
short a = 32766;
short b = 5;
short c = a + b;
//a :01111111 11111110
//b :00000000 00000101
//a,b在计算时,发生了整型提升
//a :00000000 00000000 01111111 11111110
//b :00000000 00000000 00000000 00000101
//c :00000000 00000000 10000000 00000011
//c :10000000 00000011(以short类型存储)
//c :11111111 11111111 10000000 00000011 -补码
//c :11111111 11111111 10000000 00000010 -反码
//c :10000000 00000000 01111111 11111101 -源码 = -32765
printf("%d\n", c);
return 0;
}
3)short 和 int 混合的
#include <stdio.h>
int main()
{
char b = 5;
short a = 32766;
short c = a + b;
char d = a + b;
//a :01111111 11111110
//b :00000101
//计算时,发生整型提升
//a :00000000 00000000 01111111 11111110
//b :00000000 00000000 00000000 00000101
//c :00000000 00000000 10000000 00000011
//d :00000000 00000000 10000000 00000011
//c和d的存储
//c :10000000 00000011
//d :00000011
//c和d的对外展示
//c :11111111 11111111 10000000 00000011 -补码
//d :00000000 00000000 00000000 00000011 -d = 3
//c :11111111 11111111 10000000 00000010 -反码
//c :10000000 00000000 01111111 11111101 -源码 = -32765
printf("%d\n", c);
printf("%d\n", d);
return 0;
}
4)
#include <stdio.h>
int main()
{
char a = 0xb6;
//10110110 182
//11111111 11111111 11111111 10110110
//11111111 11111111 11111111 10110101
//10000000 00000000 00000000 01001010 -74
short b = 0xb600;//10110110 00000000 //-18944
//10110110 00000000 46592
//11111111 11111111 10110110 00000000
//11111111 11111111 10110101 11111111
//10000000 00000000 01001010 00000000 -18944
int c = 0xb6000000;//10110110000000000000000000000000 =-1241513984
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)
printf("c");
//a,b需要整型提升,提升后为负数(补充符号位(最高位)),与原来的数不相等,所以不打印a和b
//c不需要整型提升,所以相等。
return 0;
}
#include <stdio.h>
int main()
{
char c = 1;
short a = 2;
printf("%u\n", sizeof(c));//sizeof内部的表达式不进行真实计算
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
printf("%u\n", sizeof(a));
printf("%u\n", sizeof(+a));
printf("%u\n", sizeof(-a));
return 0;
}
#include <stdio.h>
int main()
{
int a = 3;
char b = 1;
printf("%d\n", sizeof(b = a + 4));
printf("%d\n", sizeof(+b));
return 0;
}
算数转换:操作符的各个操作数属于不同的类型,其中一个转换另一个类型,否则无法进行
long double | 和double,float,unsigned long int,long int,unsigned int,int算数时,其他转换成long double |
double | 和float,unsigned long int,long int,unsigned int,int算数时,其他转换成double |
float | 和unsigned long int,long int,unsigned int,int算数时,其他转换成float |
unsigned long int | 和long int,unsigned int,int算数时,其他转换成unsigned long int |
long int | 和unsigned int,int算数时,其他转换成long int |
unsigned int | 和int算数时,其他转换成int |
int |
#include <stdio.h>
int main()
{
int a = 5;
float f = 5.5;
float e = a + f;//表达式有两个属性,值属性,类型属性,5是值属性,int是类型属性
printf("%f", e);//int发生了算数转换
return 0;
}
操作符的属性:
操作符的优先级
操作符的结合性
是否控制求值顺序
操作符 | 描述 | 用法示例 | 结果类型 | 结合性 | 是否控制求值顺序 |
---|---|---|---|---|---|
() | 聚组 | (表达式) | 与表达式同 | N/A | 否 |
() | 函数调用 | rexp(rexp,...,rexp) | rexp | L-R | 否 |
[ ] | 下标引用 | rexp[rexp] | lexp | L-R | 否 |
. | 访问结构成员 | lexp.member_name | lexp | L-R | 否 |
-> | 访问结构指针成员 | lexp->member_name | lexp | L-R | 否 |
++ | 后缀自增 | lexp++ | rexp | L-R | 否 |
-- | 后缀自减 | lexp-- | rexp | L-R | 否 |
! | 逻辑反 | !rexp | rexp | R-L | 否 |
~ | 按位取反 | ~rexp | rexp | R-L | 否 |
+ | 单目,表示正值 | + rexp | rexp | R-L | 否 |
- | 单目,表示负值 | -rexp | rexp | R-L | 否 |
++ | 前缀自增 | ++rexp | rexp | R-L | 否 |
-- | 前缀自减 | --rexp | rexp | R-L | 否 |
* | 间接访问 | *rexp | lexp | R-L | 否 |
& | 取地址 | &lexp | rexp | R-L | 否 |
sizeof | 取其长度,以字节表示 | sizeof rexp sizeof(类型) | rexp | R-L | 否 |
(类型) | 类型转换 | (类型)rexp | rexp | R-L | 否 |
* | 乘法 | rexp * rexp | rexp | L-R | 否 |
/ | 除法 | rexp / rexp | rexp | L-R | 否 |
% | 整数取余 | rexp % rexp | rexp | L-R | 否 |
+ | 加法 | rexp + rexp | rexp | L-R | 否 |
- | 减法 | rexp - rexp | rexp | L-R | 否 |
<< | 左移位 | rexp << rexp | rexp | L-R | 否 |
>> | 右移位 | rexp >> rexp | rexp | L-R | 否 |
> | 大于 | rexp > rexp | rexp | L-R | 否 |
>= | 大于等于 | rexp >= rexp | rexp | L-R | 否 |
< | 小于 | rexp < rexp | rexp | L-R | 否 |
<= | 小于等于 | rexp <= rexp | rexp | L-R | 否 |
== | 等于 | rexp == rexp | rexp | L-R | 否 |
!= | 不等于 | rexp != rexp | rexp | L-R | 否 |
& | 位与 | rexp & rexp | rexp | L-R | 否 |
^ | 位异或 | rexp ^ rexp | rexp | L-R | 否 |
| | 位或 | rexp | rexp | rexp | L-R | 否 |
&& | 逻辑与 | rexp && rexp | rexp | L-R | 是 |
|| | 逻辑或 | rexp || rexp | rexp | L-R | 是 |
? : | 条件操作符 | rexp? rexp : rexp | rexp | N/A | 是 |
= | 赋值 | lexp = rexp | rexp | R-L | 否 |
+= | 以...加 | lexp += rexp | rexp | R-L | 否 |
-= | 以...减 | lexp -= rexp | rexp | R-L | 否 |
*= | 以...乘 | lexp *= rexp | rexp | R-L | 否 |
/= | 以...除 | lexp /= rexp | rexp | R-L | 否 |
%= | 以...取模 | lexp %= rexp | rexp | R-L | 否 |
<<= | 以...左移 | lexp <<= rexp | rexp | R-L | 否 |
>>= | 以...右移 | lexp >>= rexp | rexp | R-L | 否 |
&= | 以...与 | lexp &= rexp | rexp | R-L | 否 |
^= | 以...异或 | lexp ^= rexp | rexp | R-L | 否 |
|= | 以...或 | lexp |= rexp | rexp | R-L | 否 |
, | 逗号 | rexp, rexp | rexp | L-R | 是 |
错误代码:
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int f = 6;
int g = a * b + c * d + e * f;//*优先级比+高,只能保证*比+在前,无法保证第三个乘法在第一个加法之前
a + --c;//只能保证--在+之前,但无法保证c是自减之前还是自减之后的
b = b-- - --b * (b = -3) * b++ + ++b;//不同编译器结果不同
int i = (++c) + (++c) + (++c);//不同编译器结果不同
return 0;
}
#include <stdio.h>
int f()
{
static int count = 1;
return ++count;
}
int main()
{
int a = f() + f() * f();//只能保证乘法在加法之前,无法保证函数调用的顺序
return 0;
}