文章目录
1.算术操作符
算术操作符:+, -, *, /, % ;
tips:(1)操作符%只能作用于整数,其余操作符可作用于整数或浮点数;
(2)操作符/两边都是整数时,执行整数除法,若有浮点型数字则执行浮点数除法;
(3)操作符/取商,操作符%取余数;
eg:5/2=2 , 5%2=1;
int main()
{
int a = 5 / 2; //整数除法
float b = 5.0 / 2; //浮点数除法
return 0;
}
2.移位操作符
移位操作符:>>(右移操作符) <<(左移操作符)
tips:两者的操作数只能是整数,且不能移动负数位;
左移操作
移动规则:左端舍去,右端补0
int main()
{
int a = 7; // 00000000000000000000000000000111
a << 1; //(左端舍去)0 00000000000000000000000000001110(右端补0)
return 0;
}
右移操作分为两种:逻辑右移:左端补0,右端舍去;
算术右移:左端补符号位,右端舍去;
int main()
{
int a = -1; // 11111111111111111111111111111111 补码
a >> 1; //逻辑右移(左端用0补) 01111111111111111111111111111111 1(最右端1舍去)
//算术右移(左端用符号位补) 11111111111111111111111111111111 1(最右端1舍去)
return 0;
}
tips:一个关于原码反码补码的小知识点
(1)符号位(最左端)为0表正数,为1表负数;
(2)正数的原码反码补码相同;
eg:输入1
原码,反码,补码:00000000000000000000000000000001
(3)负数:原码—>(符号位不变,其余取反)—>反码—>(+1)—>补码;
eg:输入-1
原码:10000000000000000000000000000001
反码:11111111111111111111111111111110
补码:11111111111111111111111111111111
(4)计算机存储的是补码,输出的是原码;计算是在补码状态下进行;
3.位操作符
位操作符:& 按位与(二进制对应位全为1时取1,其余情况取0)
| 按位或(二进制对应位全为0时取0,其余情况取1)
^ 按位异或(二进制对应位相同是取0,不同取1)
tips:位操作符的操作数必须是整数;
int main()
{
按位与&
int a = 3; //00000000000000000000000000000011
int b = 5; //00000000000000000000000000000101
int c = a & b; //00000000000000000000000000000001
printf("%d\n", c);
按位或
int a = 3; //00000000000000000000000000000011
int b = 5; //00000000000000000000000000000101
int c = a | b; //00000000000000000000000000000111
printf("%d\n", c);
按位异或
int a = 3; //00000000000000000000000000000011
int b = 5; //00000000000000000000000000000101
int c = a ^ b; //00000000000000000000000000000110
printf("%d\n", c);
return 0;
//负数操作时要以补码形式运算
}
4.赋值操作符
赋值操作符:=
复合赋值操作符:+=, -=, *=, /=, %=, >>=, <<=, &=, |=, ^=;
下面举个栗子:
int main()
{
int a = 0;
a += 2; //a= a + 2
a &= 2; //a= a & 2
a >>= 2; //a= a >> 2
return 0;
}
5.单目操作符
单目操作符: !逻辑反操作, -, +, &(取出某一变量的地址), sizeof, ~二进制按位取反, --前置后置, ++前置后置, *解引用, (类型)强制类型转换;
int main()
{
// !
int a = 7;
printf("%d\n", !a); //0 a=7为真,取反为0
printf("%d\n", !0); //1 0为假,取反为真1
return 0;
}
int main()
{
// & *
int a = 7;
int* p = &a; //将a的地址取出并存放在变量p内,存放地址的变量称为指针,变量p的类型为int*;
printf("%p\n", p); //%p打印地址
printf("%d\n", *p); //*表示解引用,即通过p中存放的地址找回a
return 0;
}
int main()
{
// --, ++前置后置
int a = 7;
printf("%d\n", ++a); //前置++:先+1,再运算 8
a = 7;
printf("%d\n", a++); //后置++:先运算,再++ 7
a = 7;
printf("%d\n", --a); //前置--:先-1,再运算 6
a = 7;
printf("%d\n", a--); //后置--:先运算,再-- 7
return 0;
}
int main()
{
// ~ 按位取反
int a = 7;
printf("%d\n", ~a); //-8
// 00000000000000000000000000000111 a是正数,原码即补码
// 11111111111111111111111111111000 ~a的补码
// 11111111111111111111111111110111 反码=补码-1
// 10000000000000000000000000001000 原码=反码符号位不变,其余取反
return 0;
}
int main()
{
//(类型)强制类型转换
int a = (int)3.14; //3.14本来是浮点型,强制转换为int类型
return 0;
}
sizeof和数组
tips:(1)sizeof用来计算变量所占内存空间的大小,单位字节;
(2)sizeof求变量名(可去括号)和变量类型达(必须有括号)到的效果一样;
int main()
{
int a = 10;
char b = 'm';
char* c = &b;
int arr[10] = { 0 };
//sizeof计算变量所占内存空间的大小,单位字节
printf("%d\n", sizeof a); //4
printf("%d\n", sizeof(int)); //4 //可通过变量名(可去括号)计算,也可通过类型(不可去括号)计算
printf("%d\n", sizeof(b)); //1
printf("%d\n", sizeof(char)); //1
printf("%d\n", sizeof(c)); //8
printf("%d\n", sizeof(char*)); //8
printf("%d\n", sizeof(arr)); //40
printf("%d\n", sizeof(int[10])); //40
return 0;
}
void test(int *arr)
{
printf("%d\n", sizeof(arr)); //8 数组传给函数时,传的是首元素地址
}
int main()
{
int arr[10] = { 0 };
printf("%d\n", sizeof(arr)); //40
test(arr);
return 0;
}
6.关系操作符
关系操作符: >, >=, <, <=, !=, ==;
tips:注意“=”和“==”,前者是赋值,后者是判断相等,一定不要写错;
7.逻辑操作符
逻辑操作符: &&逻辑与, ||逻辑或;
tips:(1)&&两个操作数都为真时为1,||两个操作数都为假时为0;
(2)注意区分逻辑与&&和按位与&,逻辑或||和按位或|;
int main()
{
int a = 1, b = 2;
printf("%d\n", a && b); //1
printf("%d\n", a & b); //0
printf("%d\n", a || b); //1
printf("%d\n", a | b); //3
return 0;
}
一道小试题
int main()
{
int i = 0, a = 1, b = 2, c = 3, d = 4;
i = a++ && ++b && d++; //2 3 3 5
i = a++ || ++b || d++; //2 2 3 4 a=1为真,后面无需运算,不管是什么都是真
printf("a=%d,b=%d,c=%d,d=%d", a, b, c, d);
return 0;
}
8.条件操作符
条件操作符:exp1? exp2: exp3
(是否满足语句exp1,满足则输出语句exp2,否则输出语句exp3)
int main()
{
int a = 10;
int b = 15;
int c = (a > b ? a : b);
printf("%d\n", c);
return 0;
}
9.逗号表达式
逗号表达式:exp1,exp2,……,expn
tips:(1)从左到右依次执行,整个表达式的结果是最后一个表达式的结果;
(2)注意:expn之前的语句也会执行;
int main()
{
int a = 2;
int b = 3;
int c = (a < b, a += 1, b+=2, a = b + 10);
printf("%d\n", c); //15
return 0;
}
a = value();
make(a);
while (a > 0)
{
//执行任务;
a = value();
make(a);
}
//上述代码可等价为:
while (a = value(), make(a), a > 0)
{
//执行任务
}
10.下标引用,函数调用和结构成员
10.1下标引用
[ ] 下标引用操作符
操作数:数组名+一个索引值;
int main()
{
int arr[10] = { 0 };
arr[0] = 5; //[]de]]的两个操作数:arr 和 0
return 0;
}
10.2函数调用
( )函数调用操作符
操作数:接受一个或多个操作数,第一个操作数是函数名,其余操作数是传给函数的参数;
tips:至少有一个操作数,即函数名;
void test1() //此处()是定义函数时的语法规则
{
printf("hello world\n");
}
int test2(const char* str) //此处()是定义函数时的语法规则
{
printf("%s\n",str);
}
int main()
{
test1(); //使用()作为函数调用操作符(只有1个操作数test)
test2("hello world"); //使用()作为函数调用操作符
return 0;
}
10.3结构成员
访问一个结构成员:
操作符 . 结构体.成员名;
操作符—> 结构体指针—>成员名;
struct stu //创建一个结构体类型——类型名:struct stu
{
char name[20];
int age;
char id[20];
};
int main()
{
struct stu s1 = { "张三",20,"20203402" };
struct stu* ps = &s1;
printf("%d\n", (*ps).age); //结构体变量.成员们 操作符.
printf("%d\n", ps->age); //结构体指针->成员们 操作符->
printf("%s\n", (*ps).name);
printf("%s\n", s1.name);
return 0;
}
11.表达式求值
表达式求值的顺序是由操作符的优先级和结合性决定的;
有些表达式的操作数在计算时需要转化为其他类型,下面介绍两种常见转换;
11.1隐式类型转换
整型提升:c的整型算术运算总是以缺省整型类型的精度来进行的,
为了获得这个精度,我们在计算时通常要将表达式中的字符型,短整型操作数在使用前转化为整型;
如何进行整型提升:
一、有符号的整型提升:
1.对于负数: char a=-1
-1的补码为11111111
用符号位进行提升11111111111111111111111111111111
2.对于正数: char b =1
1的补码为00000001
用符号位进行提升00000000000000000000000000000001
二、无符号的整型提升:高位补0;
int main()
{
//有符号数,整型提升用符号位补
char a = 3;
//00000000000000000000000000000011
//00000011——a 截断
char b = 127;
//00000000000000000000000001111111
//01111111——b 截断
char c = a + b;
//a b如何相加
//00000011——a 整型提升00000000000000000000000000000011
//01111111——b 整型提升00000000000000000000000001111111
//提升00000000000000000000000010000010
//截断10000010——c
//整型提升11111111111111111111111110000010 补码
//11111111111111111111111110000001 反码
//10000000000000000000000001111110 原码
//-126
printf("%d\n", c);
return 0;
}
int main()
{
char a = 1;
printf("%d\n", sizeof(a)); //1
printf("%d\n", sizeof(+a)); //4
printf("%d\n", sizeof(-a)); //4
return 0;
}
tips:(1)cahr short型操作数只要参与表达式运算就会发生整型提升;
(2)有符号数提升时补符号位,无符号数补0;
11.2算术转换
算术转换:当某个操作符的各个操作数都不是相同类型时,操作无法进行,需要将其中一个操作数的类型转化为与另一个操作数相同的类型时才能进行运算;
long double
double
float
unsigned long int
long int
unsigned int
int
当两个操作数类型不同时,需将操作数类型在上表位置更低的操作数的类型转化为操作数类型位置更高的类型;否则会有精度丢失;
11.3操作符属性
复杂表达式求值的三个影响因素:
1.操作符的优先级;
2.操作符的结合性;
3.是否控制求值顺序;