算术操作符 + - * / %
/
3/2 结果是整数1(除号两端都是整数执行整数除法)
3.0/2 3/2.0 结果是小数(除号两端至少有一个是小数执行小数除法)
%
9%2 求的是余数(取模操作符两端必须是整型)有范围
移位操作符
操作的是二进制位
<< 左移操作符
>> 右移操作符
注意:不能移动负数位(num>>-1)
左移
int main()
{
int a= 5;
int b = a << 1;//左边丢弃,右边补0
printf("%d\n", a);//5 00000000000000000000000000000101
printf("%d\n", b);//10 00000000000000000000000000001010
return 0;
}
int main()
{
int a= -5;
int b = a << 1;
printf("%d\n", a);//-5 11111111111111111111111111111011
printf("%d\n", b);//-10 11111111111111111111111111110110补码
// 11111111111111111111111111110101反码
// 00000000000000000000000000001010原码
return 0;
}
计算的是补码,打印出来的是原码
原码 --> 符号位不变其他位按位取反得到反码 --> 反码+1得到补码
符号位不变其他位按位取反得到原码 <-- 补码-1得到反码 <-- 补码
右移
右移操作符(到底用哪个方法取决于编译器):
- 算术右移: 右边丢弃,左边补原符号位 (当前编译器在右移采用算术右移)
- 逻辑右移: 右边丢弃,左边补0
int mian()
{
int a = 5;
int b = a >> 1;
printf("%d\n", a);//5 00000000000000000000000000000101
printf("%d\n", b);//2 00000000000000000000000000000010
return 0;
}
int mian()
{
int a = -5;
int b = a >> 1;
printf("%d\n", a);//-5
printf("%d\n", b);//-3
return 0;
}
整数的二进制表示形式有3种:
例: int a = 5
原码 00000000000000000000000000000101
反码 0000000000000000000000000000101
补码 00000000000000000000000000000101
int a = -5
原码: 10000000000000000000000000000101
反码:11111111111111111111111111111010(原码的符号位不变,其他位按取反(0和1反一反)得到的就是反码)(补码-1就是反码)
补码:11111111111111111111111111111011 (反码+1就是补码)
符号位:
0 整数
1 负数
正整数的原码、反码、补码是相同的
负整数的原码、反码、补码是要计算的
整数在内存中存储的是补码的二进制
位操作符:
& - 按(二进制)位与
| - 按(二进制)位或
^ - 按(二进制)位异或
a&b 双目操作符
&a 单目操作符
int main()
{
int a = 3;//00000000000000000000000000000011补码
int b = -5;//11111111111111111111111111111011补码
int c = a & b;//00000000000000000000000000000011补码(两个同时为1时按位与才能是1)
printf("%d\n", c);//3 正数原反补相同
return 0;
}
int main()
{
int a = 3;
int b = -5;
int c = a | b;//(按位或只要有1就是1)
//11111111111111111111111111111011补码
printf("%d\n", c);//-5 (恰好是-5的补码和原码)
return 0;
}
int main()
{
int a = 3;
int b = -5;
int c = a ^ b;//(异或:对应的二进制位是相同为0,相异为1)
//11111111111111111111111111111000补码
//11111111111111111111111111110111反码(补码-1)
printf("%d\n", c);//-8 10000000000000000000000000001000原码
return 0;
}
a^a = 0
0^a = a
不能创建临时变量(第三个变量)实现两个变量的交换
int main()
{
int a = 3;//011
int b = 5;//101
printf("a=%d b=%d\n", a, b);
a = a ^ b;//110
b = a ^ b;//011
a = a ^ b;//101
printf("a=%d b=%d\n", a, b);
return 0;
}
求一个整数存储在内存中的二进制中1的个数
int main()
{
int num = 0;
scanf("%d", &num);
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
if ((num >> i) & 1)
{
count++;
}
}
printf("%d\n", count);
return 0;
}
赋值操作符
=
连续赋值:a = x = y+1;(可读性较差)
复合赋值符
a+= a-= ……
单目操作符
! 逻辑反操作
C语言中0表示假,非0表示真
C语言中C99之前没有表示真假的类型
C99中引用了布尔类型 false true
- 负值
+ 正值
& 取地址
* 间接访问操作符(解引用操作符)
struct S
{
char name[20];
int age;
};
int main()
{
int a=10;
int*pa=&a;
*pa=20;//解引用操作符
//*&a ==> a
int arr[10]={0};
&arr;//取出数组地址,数组的地址应该放到【数组指针】中去
struct S s = {0};
struct S* ps = &s;
return 0;
}
sizeof 操作数的类型长度(以字节为单位)
是操作符,不是函数
用来计算类型创建的变量所占内存的大小
int main()
{
/*int a = 10;
printf("%d\n", sizeof(a));//4
printf("%d\n", sizeof(int));//4*/
//int arr[10] = { 0 };
//printf("%d\n", sizeof(arr));//40(前提是数组没有传参)
int a = 10;
short s = 0;
printf("%d\n", sizeof(s = a + 2));//2
printf("%d\n", a);//10
//sizeof()中的表达式不参与计算
//sizeof是在编译期间处理的
printf("%d\n", s);//0
return 0;
}
~ 对一个数的二进制按位取反
int main()
{
/*int a = 0;
printf("%d\n", ~a);*///-1
//00000000000000000000000000000000
//11111111111111111111111111111111 补码
//11111111111111111111111111111110 反码
//10000000000000000000000000000001 取反 -1
int a = 11;
//00000000000000000000000000001011
//00000000000000000000000000000100
a |= (1 << 2);
printf("%d\n", a);//15 1111
a &= (~(1 << 2));
printf("%d\n", a);//11 1011
return 0;
}
~ (-1) --> 0
为什么while(~scanf("%d",&n))可以终止循环?
scanf()读取失败的时候,返回EOF(-1)
原来写法: while(scanf("%d", &n) !=EOF)
-- 前置、后置--
++ 前置、后置++
++a 前置++ 先++,后使用
a++ 后置++ 先使用,后++
int a=10;
int b=a+1;
int b=++a;//前置++//a=a+1;b=a;
int b=a++;//后置++//int b=a;a=a+1;
#include <stdio.h>
int main()
{
int a, b, c;
a = 5;
c = ++a;// ++a:加给a+1,结果为6,用加完之后的结果给c赋值,因此:a = 6 c = 6
b = ++c, c++, ++a, a++;
// 逗号表达式的优先级,最低,这里先算b=++c, b得到的是++c后的结果,b是7
// b=++c 和后边的构成逗号表达式,依次从左向右计算的。
// 表达式结束时,c++和,++a,a++会给a+2,给c加1,此时c:8,a:8,b:7
b += a++ + c; // a先和c加,结果为16,在加上b的值7,比的结果为23,最后给a加1,a的值为9
printf("a = %d b = %d c = %d\n:", a, b, c); // a:9, b:23, c:8
return 0;
}
(类型) 强制类型转换
int main()
{
int a = (int)3.14;
printf("%d\n", a);//3
return 0;
}
关系操作符
比大小
> >= < <= != ==
两个字符串是不能比大小的
实际比的是两个字符串中首字母的地址
strcmp-专门用来比较字符串的大小
对应位置上字符的大小,而不是比较长度
逻辑操作符
&& 逻辑与
|| 逻辑或
只在乎真假
区分:
1&2 0
1&&2 1
1|2 3
1||2 1
条件操作符
exp1 ? exp2 : exp3
表达式1为真,表达式2执行,表达式2的结果是整个表达式的结果
表达式1为假,表达式3执行,表达式3的结果是整个表达式的结果
逗号表达式
exp1,exp2,exp3,…expn
从左到右依次执行,整个表达式的结果是最后一个表达式的结果
下标引用、函数调用和结构成员
[ ]下标引用操作符
操作数:一个数组名+一个索引值
int main()
{
int arr[10]={0};//创建数组
arr[9] = 10;//实用下标引用操作符
}
[ ]的两个操作数是arr和9
等价于:
arr[4]<==>*(arr+4)<==>*(4+arr)<==>a[arr]
( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
int Add(int x,int y)
{
return x+y;
}
int main()
{
int ret = Add(2,3);//()函数调用操作符,操作数就是:Add,2,3
printf("%d\n",ret);
return 0;
}
访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
struct Stu
{
char name[20];
int age;
float score;
};
void print1(struct Stu ss)
{
printf("%s %d %f\n", ss.name, ss.age, ss.score);
}
//结构体变量.成员名
void print2(struct Stu* ps)
{
//printf("%s %d %f\n",(*ps).name,(*ps).age,(*ps).score);
printf("%s %d %f\n", ps->name, ps->age, ps->score);
}
//结构体指针->成员名
int main()
{
struct Stu s = { "张三",20,90.5f };
strcpy(s.name, "李四");//修改用strcpy
//scanf("%s",s.name);
print1(s);
print2(&s);
return 0;
}
表达式求值
表达式求值的顺序一部分是由操作度的优先级和结合性决定
有些表达式操作数在求值过程中可能需要转换为其他类型。
隐式类型转换
C语言的整型算术总是以缺省整型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
int main()
{
char c1 = 3;
//00000000000000000000000000000011
//00000011 c1
char c2 = 127;
//00000000000000000000000001111111
//01111111 c2
char c3= c1 + c2;
//10000010 c3 补码
//111111111111111111111111110000010提升
//111111111111111111111111110000001反码
//100000000000000000000000001111110补码
printf("%d\n", c3);//-126
return 0;
}
char == ? 到底是signed char还是unsigned char取决于编译器
signed char
unsigned char
short == signed short
unsigned short
int == signed int
unsigned int
整型提升
负数补1,正数补0
int main()
{
char c = 1;
printf("%u\n",sizeof(c));//1
printf("%u\n",sizeof(+c));//4
printf("%u\n",sizeof(-c));//4
return 0;
}
//c只要参与表达式运算就会发生整型提升
算术转换
long double
double
float
unsigned long int
long int
unsigned int
int
操作符的属性
复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性(从左到右或者从右到左)
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。