操作符
1.操作符分类
算术操作符
移位---
位---
赋值---
单目---
关系---
逻辑---
条件---
逗号表达式
下标引用,函数调用和结构成员
2.算术操作符
/ % + - *
(1)除了%操作符之外,其他的几个操作符可以作用于整数和浮点数。
(2)对于/操作符,如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
(3)%操作符的两个操作符必须为整数。返回的是整除之后的余数
3.移位操作符
移动的是二进制位(整数在内存中存储的是补码)
<< 左移操作符
右移操作符 >>
移位操作符的操作数只能是整数,不能是浮点数!!!
3.1左移操作符
移位规则:
左边抛弃,右边补0
#include<stdio.h>
int main()
{
int a = 7;
int b = a << 1;
printf("a=%d\n", a);
printf("b=%d\n", b);
return 0;
}
左移操作符有*2的效果
#include<stdio.h>
int main()
{
int a = -7;
int b = a << 1;
printf("a=%d\n", a);
printf("b=%d\n", b);
return 0;
}
3.2 右移操作符
算术移位:右边丢弃,左边补原符号位
逻辑移位:右边丢弃,左边补0
对于移位运算符,不要移动负数位,这个是标准未定义的
int b = a>>-1;
VS编译器(大部分)采用的算术右移
正数测不出来是哪种移位方式
#include<stdio.h>
int main()
{
int a = 7;
int b = a >> 1;
printf("a=%d\n", a);
printf("b=%d\n", b);
return 0;
}
负数:
#include<stdio.h>
int main()
{
int a = -7;
int b = a >> 1;
printf("a=%d\n", a);
printf("b=%d\n", b);
return 0;
}
4. 位操作符
&//按(2进制)位与
|//按位或
^//按位异或
它们的操作数必须是整数
#include<stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a & b;
//00000000000000000000000000000011 3的补码
//10000000000000000000000000000101 -5的原码
//11111111111111111111111111111010 -5的反码
//11111111111111111111111111111011 -5的补码
//00000000000000000000000000000011 3的补码
//11111111111111111111111111111011 -5的补码
//00000000000000000000000000000011 正数的原码,反码,补码相同
//只要有0,按位与的结果就是0,两个都为1,按位与的结果才为1
//%d 意味着打印一个有符号的整数(原码)
printf("c=%d\n", c);
return 0;
}
#include<stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a | b;
//00000000000000000000000000000011 3的补码
//10000000000000000000000000000101 -5的原码
//11111111111111111111111111111010 -5的反码
//11111111111111111111111111111011 -5的补码
//00000000000000000000000000000011 3的补码
//11111111111111111111111111111011 -5的补码
//11111111111111111111111111111011 补码
//11111111111111111111111111111010 反码
//10000000000000000000000000000101 原码
//-5
//只要有1,按位或的结果都是1,两个都为0,按位或的结果才是0
//%d 意味着打印一个有符号的整数(原码)
printf("c=%d\n", c);
return 0;
}
#include<stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a ^ b;
//00000000000000000000000000000011 3的补码
//10000000000000000000000000000101 -5的原码
//11111111111111111111111111111010 -5的反码
//11111111111111111111111111111011 -5的补码
//00000000000000000000000000000011 3的补码
//11111111111111111111111111111011 -5的补码
//11111111111111111111111111111000 补码
//11111111111111111111111111110111 反码
//10000000000000000000000000001000 原码
//-8
//相同为0,相异为1
//%d 意味着打印一个有符号的整数(原码)
printf("c=%d\n", c);
return 0;
}
//3^3=0 ->a^a=0
//011
//011
//000
//0^5=5 ->0^a=a
//000
//101
//101
//3^3^5=5
//3^5^3=5
//异或操作符支持交换律
//011
//101
//110
//011
//101
//该算法可以避免溢出
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
printf("交换前:a=%d b=%d\n", a, b);
a = a ^ b;//a=3^5
b = a ^ b;//3^5^5 --->b=3
a = a ^ b;//3^5^3 --->a=5
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
5.赋值操作符
int weight = 120;//体重 初始化
weight = 89;//不满意就赋值
a = x = y+1;//连续赋值(不推荐)
x = y+1;
a = x;//这样的写法更加清晰爽朗且易于调试
复合赋值符
+= -= *= /= %= >>= <<= &= |= ^=
6.单目操作符
! 逻辑反操作符 真变为假 假变为真 0表示假,非0表示真
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
sizeof 是操作符,不是函数,后面的()可以省略,如:sizeof a 但sizeof int 的写法是错误的
// strlen是库函数,用来求字符串长度
~ 对一个数的二进制按位取反
-- 前置,后置--
++ 前置,后置++
* 间接访问该操作符(解引用操作符)
(类型) 强制类型转换
#include<stdio.h>
int main()
{
int flag = 3;
//flag 为真,进入if
if (flag)
{}
//flag为假,进入if
if(!flag)
{}
int a=-10;
int b=+a;//-10
int b=-a;//10
&a;//取出a在内存中的起始地址 a占4个字节,取出的是第一个字节的地址
int* p = &a;//p就是指针变量
int a=10;
int n=sizeof(a);//计算的是a所占内存的大小,单位是字节
int n=sizeof(int);
//计算的是变量/类型所创建的变量
//~是按(二进制)位取反 将0变为1,将1变为0
int a=0;
//00000000000000000000000000000000
//11111111111111111111111111111111 ~a补码
//11111111111111111111111111111110 ~a反码
//10000000000000000000000000000001 ~a原码
//-1
printf("%d\n",~a);
int a = 3;
int b = ++a;//先++,后使用
printf("%d\n",a);//4
printf("%d\n",b);//4
int a = 3;
int b = a++;//先使用,后++
printf("%d\n",a);//4
printf("%d\n",b);//3
return 0;
}
#include<stdio.h>
int main()
{
int a = 10;
printf("%d\n", a--);//先使用,只会打印出10
printf("%d\n", a);//后--,打印出9
return 0;
}
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;
*p = 20;//指向的是p所存放地址的对象 即a,将a的值改为20
printf("%d\n", a);
return 0;
}
#include<stdio.h>
int main()
{
int a = (int)3.14;
printf("%d\n", a);
return 0;
}
7.关系操作符
==
int main()
{
if("abc"=="abcdef")//这样是在比较两个字符串的首字符的地址
{
}
//两个字符串比较相等应该使用strcmp(库函数)
return 0;
}
8.逻辑操作符
&& 逻辑与
|| 逻辑或
只关注真假
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
int c = a && b;//并且 如果表示真,结果为1 表示假,为0
int c = a || b;//或者 只要有一个为真,结果为1
printf("%d\n", c);
//if (a && b)
//{
//}
return 0;
}
&& 左边为假,右边就不计算了
|| 左边为真,右边就不计算了
9.条件操作符
三目操作符
(表达式1)?(表达式2):(表达式3)
(a>5)?(b=3):(b=-3)
b=(a>5?3:-3);
10.逗号表达式
就是用逗号隔开的多个表达式
从左至右依次执行,整个表达式的结果是最后一个表达式的结果
int main()
{
int a = 1;
int b = 2;
int c = (a>b,a = b+10,a,b = a+1);
printf("c=%d",c);
return 0;
}
11.下标引用操作符,函数调用和结构成员
(1)下标引用操作符
[]
int arr[10]={0};
arr[7]=8;//--->*(arr+7) arr是数组首元素的地址 arr+7就是跳过7个元素,指向了第8个元素 *(arr+7)就是第八个元素
//arr[7]-->*(arr+7)-->*(7+arr)-->7[arr]
7[arr]=9;
(2)函数调用
int Add(int x,int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
//函数调用
int c = Add(a,b);//()就是函数调用操作符
return 0;
}
(3)结构成员
. 结构体.成员名
-> 结构体指针->成员名
struct Stu
{
char name[20];
int age;
double score;
}
void set_stu(struct Stu ss)
{
strcpy( ss.name,"zhangsan" );
ss.age=20;
ss.score=100.0;
}
viod print_stu(struct Stu ss)
{
printf("%s %d %lf",ss.name,ss.age,ss.score);
}
int main()
{
struct Stu s ={0};
set_stu(s);
print_stu();
return 0;
}
struct Stu
{
char name[20];
int age;
double score;
}
viod set_stu(struct Stu* ps)
{
//strcpy((*ps).name,"zhangsan");
//(*ps).age=20;
//(*ps).score=100.0;
strcpy(ps->name,"zhangsan");
ps->age=20;
ps->score=100.0;
}
void set_stu(struct Stu* ps)
{
strcpy( ps->name,"zhangsan" );
ps->age=20;
ps->score=100.0;
}
viod print_stu(struct Stu* ps)
{
printf("%s %d %lf",ps->name,ps->age,ps->score);
}
int main()
{
struct Stu s ={0};
set_stu(&s);
print_stu(&s);
return 0;
}
结构体指针->成员
结构体对象.成员
12.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
12.1隐式类型转换
c的整型算术运算总是至少以缺省整型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
无符号数整型提升时高位补0
#include<stdio.h>
int main()
{
char a = 5;
//00000000000000000000000000000101
//00000101 a
char b = 126;
//00000000000000000000000001111110
//01111110
char c = a + b;
//00000000000000000000000000000101 a
//00000000000000000000000001111110 b
//00000000000000000000000010000011
//10000011 c
//11111111111111111111111110000011 补码
//11111111111111111111111110000010 反码
//10000000000000000000000001111101 原码
printf("%d\n",c);//-125
return 0;
}
#include<stdio.h>
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));//+c 也是一个表达式,会进行整型提升
printf("%u\n", sizeof(-c));
return 0;
}
12.2算术转换
如果某个操作数的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
向上(大的类型)转换
long double
double
float
unsigned long int
long int
unsigned int
int
12.3操作符的属性
复杂表达式的求值有三个影响的因素
1.操作符的优先级(相邻)
2.操作符的结合性
3.是否控制求值顺序(&& || ?: (,,)是,其他都为否)
问题表达式
a*b+c*d+e*f
c + -- c