目录
算术操作符(运算符):
+ - * / %
在算术运算符中,+ - * 跟我们数学中的运算是一样的
在这里主要说一下 / 跟 % 这两个操作符
1、/ (除法)
在除法运算中,若除号两边的操作数都为整型,则结果是取商的部分
操作数:指的就是 被除数 与 除数
eg:a/b a和b均为整数,若a<b时 此结果为0
(小数除以大数结果为0)
在两个整数运算中C语言遵从的是(向0取整)法则
所谓向0取整 就是在 数轴上朝向0的方向取整
eg : 10/8=1.25 数学运算
10/8 =1 C语言
因为1.25>0 在取整的时候不会取到整数2 而是取到1 因为1 比2更接近与0
在除法运算中,只要有一个操作数是浮点数,就是浮点数的除法运算
所谓浮点数的除法运算就是,最后的结果是一个浮点数
#include <stdio.h>
int main()
{
printf("%d\n",20/10);//结果就是一个是整数
//小数除大数
printf("%d\n",2/19);//结果为0
printf("%lf\n",20.0/10);//结果是一个浮点数
printf("lf\n",20.0/10.0);//结果是一个浮点数
return 0;
}
2、% (取模、取余)
在取模运算中 取到的结果是两数相除之后所留的余数
取余运算两边的操作符只能是整数
在模运算中,a%b 当a的值小于b时 结果为a
(小数模大数结果为小数本身)
#include <stdio.h>
int main()
{
//两数进行模运算
printf("%d\n", 5 % 4); //1
//小数 模 大数 得到的是小数本身
printf("%d\n", 3 % 4); //3
printf("%d\n", 2 % 19); //2
return 0;
}
移位操作符:
<< (左移) >>(右移)
注意:移位操作符的操作数只能是整数
1、<< 左移操作符:
移动的时二进制位,因为在内存中数据是以补码的形式存在的,同理 移动的是二进制的补码
左移操作符如何移动:左边丢弃,右边补0
移位操作符不能移动负数位
#include <stdio.h>
int main()
{
//左移操作符 <<
int a = 10;
//a 的原码:00000000 00000000 00000000 00001010
//正数的原码 反码、补码 相同
int b = a << 1;
//将a左移一位:00000000 00000000 00000000 00010100
// 左移一位之后还是一个正数,原、反、补 相同
// b=20
printf("%d\n", b);
}
2、>>右移操作符:
移动的是二进制位 移动的是二进制位的补码
移位规则:
1、算术右移:右边丢弃左边补符号位(大部分编译器都用)
2、逻辑右移:右边丢弃左边补0
#include <stdio.h>
int main()
{
//0 表示正数,1表示负数
int a = -10;
//a原码 : 10000000 00000000 00000000 00001010
// 反码: 11111111 11111111 11111111 11110101
// 补码: 11111111 11111111 11111111 11110110
//将补码算术右移一位
int b = a >> 1;
//补码:11111111 11111111 11111111 11111011
// 10000000 00000000 00000000 00000100
//原码:10000000 00000000 00000000 00000101
// b=-(1+4)=-5
printf("%d\n", b);
return 0;
}
位操作符
& | ^ ~
注意:它们的操作数必须是整数
1、& 按位与
& 按位与:操作的是二进制的补码,二进制位一位一位的运算
运算规则:两个对应的二进制位只要有0结果为0,只有全为1时结果为1
(有0为0,全为1为1)
#include <stdio.h>
int main()
{
//&:有0为0 同为1为1
int a = 10;
int b = 20;
int c = a & b;
printf("%d\n", c);
//正数的 原码、反码、补码相同
//a原码:00000000 00000000 00000000 00001010
//b原码:00000000 00000000 00000000 00010100
//a&b: 00000000 00000000 00000000 00000000
//c=0;
return 0;
}
2、| 按位或
| 按位或:操作的是二进制的补码,二进制位对齐运算
运算规则:对应的二进制位 只要有一个 1 结果为 1 ,都为 0 结果为 0
(有1为1 ,同0为0)
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = a | b;
printf("%d\n", c);
//正数的原码、反码、补码相同
//a的补码: 00000000 00000000 00000000 00001010
//b的补码:00000000 00000000 00000000 00010100
// a | b: 00000000 00000000 00000000 00011110
//c=2+4+8+16=30
return 0;
}
3、^ 按位异或
^ 按位异或:操作的是二进制位的补码,二进制位对齐运算
运算规则:二进制位相同为 0 ,二进制位不同为 1
(相同为0,相异为1)
任何数和0异或,结果都是任何数本身
任何数异或其本身 结果都是0
#include <stdio.h>
int main()
{
int a = 0;
int b = 10;
int c = a ^ b;
printf("%d\n", c);
//正数的原码、反码、补码 相同
//a 补码:00000000 00000000 00000000 00000000
//b 补码:00000000 00000000 00000000 00001010
//c 补码:00000000 00000000 00000000 00001010
//c是正数,原码、反码、补码 相同
//c=10
//总结:任何数 和 0 异或 结果都是其本身
return 0;
}
不创建第三方变量交换两个值(^ 具有结合律)
#include <stdio.h>
int mian()
{
//不使用第三方变量,交换值
int a = 10;
int b = 0;
a = a ^ b;
b = a ^ b;
a = a ^ b;
//用等量代换方法
// a=a^b;
// b=a^b^b; b^b=0 ==> b=a
// a=a^b^a a^a=0 ==> a=b
//按位异或具有结合律
return 0;
}
4、~ 按位取反
~ 按位取反:操作的是二进制位的补码
运算规则:将二进制位的 0变成1 ,1变成0
#include <stdio.h>
int main()
{
int a = -1;
int b = ~a;
printf("%d\n", b);
//a:
//原码:10000000 00000000 00000000 00000001
//反码:11111111 11111111 11111111 11111110
//补码:11111111 11111111 11111111 11111111
// ~a: 00000000 00000000 00000000 00000000
//~a 符号位是0 是正数,正数的原 反 补 相同
// ~a 原码 :00000000 00000000 00000000 00000000
// b=0;
return 0;
}
赋值操作符
1、= 赋值符号
就是给一个变量赋值
2、复合赋值符
1> += 、-=、/=、%=
#include <stdio.h>
int main()
{
// +=
int a = 100;
a += 11; //a=a+11 a=111
printf("%d\n", a);
// -=
int b = 90;
b -= 20; //b=b-20 //b=70
printf("%d\n", b);
// *=
int c = 10;
c *= 2; // c=c*2 c=20
printf("%d\n", c);
// /=(除等于)
int d = 30;
d /= 10; // d=d/10 d=3
printf("%d\n", d);
// %=
int e = 120;
e %= 6; // e=e%6 e=0
printf("%d\n", e);
}
2> >>= 、 <<=
#include <stdio.h>
int main()
{
// <<=
int a = 10;
a <<= 2; // a=a<<2 a=40
//a 补码: 00000000 00000000 00000000 00001010
//a<<2 : 00000000 00000000 00000000 00101000
printf("%d\n", a);
// >>=
int b = 20;
b >>= 2; // b=b>>2 b=5
//b补码:00000000 00000000 00000000 00010100
//b>>2 :00000000 00000000 00000000 00000101
printf("%d\n", b);
return 0;
}
3> &=、|=、^=
int main()
{
// &=
int a = 10;
a &= 2; // a=a&2 a=2
printf("%d\n", a);
// a补码:00000000 00000000 00000000 00001010
// 2补码:00000000 00000000 00000000 00000010
// a & 2:00000000 00000000 00000000 00000010
// &(按位与):有0为0 同1为1
// |=
int b = 20;
b |= 12; // b=b | 12 b=28
printf("%d\n", b);
// | (按位或) :有1为1,同0为0
// b补码:00000000 00000000 00000000 00010100
//12补码:00000000 00000000 00000000 00001100
//b | 12:00000000 00000000 00000000 00011100
// ^=
int c = 30;
c ^= 16; // c=c^16 c=14
printf("%d\n", c);
// ^ 相同为0 相异为1
// c补码:00000000 00000000 00000000 00011110
//16补码:00000000 00000000 00000000 00010000
//c ^ 16:00000000 00000000 00000000 00001110
return 0;
}
单目操作符
操作数只有一个的操作符号
!:逻辑反操作符
- :负值
+ :正值
& :取地址
sizeof 操作符
sizeof 操作符 是一个操作符,要区分它与函数
sizeof 主要用于计算数据类型在内存中的大小(单位是字节)
在计算变量的大小的时候sizeof的括号可以省略
~ :对一个数的二进制位按位取反
#include <stdio.h>
int main()
{
// ! 逻辑反
if (!1)
printf("!1为真\n");
else
printf("!1为假\n");
// - 、+ 号
int a = -10;
a=-a;
printf("%d\n", a);
int b = 20;
b = +b;
printf("%d\n", b);
// & 取地址
int c = 30;
printf("%p\n", &c);
// sizeof操作符
int d = 40;
printf("%d\n", sizeof(d));
printf("%d\n", sizeof (int));//类型的括号不能省略
printf("%d\n", sizeof d);//括号可以省略
// ~ 按位取反 只能运算与整数
int e = 50;
printf("%d\n", e);//未操作前
int f = ~e;// 对e 按位取反 赋值给f
//f=-(1+2+16+32) f=-51
printf("%d\n", f);
// e补码:00000000 00000000 00000000 00110010
//~e补码:11111111 11111111 11111111 11001101
// :10000000 00000000 00000000 00110010
//~e原码:10000000 00000000 00000000 00110011
return 0;
}
sizeof与数组
sizeof(数组名):表示整个数组在内存中所占空间的大小 单位是字节
sizeof 在计算数组大小 的时候 在函数内跟函数外是有区别的
#include <stdio.h>
void MAX(int arr[])
{
int b = sizeof(arr);
// 在32位下是4 在64位下是8
printf("%d\n", b);
//因为数组在传参的时候传的是数组名,数组名是首元素地址
//所以在传参的时候传的是指针
// 函数里面得用指针来接受,我们只是将形参写成了数组的形式,但其本质上是一个指针
//指针在32位下占4个字节,在64位下占个字节
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
//计算整个数组的大小
int a = sizeof(arr);
printf("%d\n", a);
//调用函数 函数实参是数组名
MAX(arr);
return 0;
}
- - 自减
前置--:先--,后使用 (先让变量自减1,再使用)
后置--:先使用,后-- (先使用,再让变量自减1)
#include <stdio.h>
int main()
{
//前置--
int a = 10;
int b = --a;
//此代码执行了两部操作
// 第一步:a=a-1;
// 第二步:b=a;
printf("%d\n", b);//b的值为9
//后置--
int c = 20;
int d = c--;
//此代码也执行了两步操作
// 第一步:d=c;
// 第二步:c=c-1;
printf("%d\n", d);//d的值为20
return 0;
}
++ 自增
前置++:先++后使用(先让变量自增1,再使用)
后置++:先使用后++(先使用,再让变量自增1)
#include <stdio.h>
int main()
{
//前置++
int a = 10;
int b = ++a;
//分为两步
// a=a+1;
// b=a;
// 先让a自增1 再使用a 把a的值赋给b
printf("%d\n", b);//b的值为11
//后置++
int c = 20;
int d = c++;
//分为两步
// d=c;
// c=c+1;
//先使用c 把c的值赋给d 再让c自增1
printf("%d\n", d);//d的值为20
return 0;
}
* 解引用操作符
解引用操作符,也被叫做间接访问操作符,运用于指针
作用:将指针变量进行解引用操作,就是取到指针变量所指向的内容
#include <stdio.h>
int main()
{
int a = 10;
//定义一个整型指针变量,里面存放的是整型变量a的地址
int* p = &a;
//我们将指针变量解引用操作
// 取到的是指针变量p所指向的内容 也就是a
printf("%d\n", *p);// 10
//同理:*p 其实就是 a
//也可给其赋值
*p = 20;
//此时a的值被赋值成20
printf("%d\n", a);// 20
return 0;
}
(类型) 强制类型转换
强制类型转换就是将一种类型的数据,强制转换成另一种类型的数据
注意:当大类型往小类型转换时会发生截断
当 float 或者 dauble 类型强转为 int 类型时: 发生截断 取到的是整数部分
当 char 类型 强转为 int 类型时:取到的是其对于的 ACSII码值
#include <malloc.h>
#include <stdio.h>
int main()
{
// float 转 int 截断取整数部分
float a = 3.14f;
int b = (int)a;
printf("%d\n", b);// b为3
// double 转 int 截断取整数部分
double d = 4.225;
int e = (int)d;
printf("%d\n", e);// e为4
// char 转 int 取到ASCLL值
char c = 'A';
int f = (int)c;
printf("%d\n", f);// f 为 65
//动态内存开辟
// 将在堆区上开辟的空间强制转化为int* 类型 赋给 指针p
int* p = (int*)malloc(sizeof(int)* 13);
//时间戳产生随机数
//将time 函数的返回值强制转化成 unsinged int (无符号整型)
// srand 函数的参数是一个无符号整型所以强制类型转换
srand((unsigned int)time(NULL));
int x = rand();//产生一个 0 - 32767 的随机数
printf("%d\n", x);//每次打印出现的结果都不一样
return 0;
}
关系操作符
>、>=、<、<=、==、!=
跟数学中的判断一样,
不过在这里多了一个 != (不等于):判断两个数是否不相等
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
if (a > b)
{
printf("a>b\n");
}
if (a < b)
{
printf("a<b\n");
}
if (a == b)
{
printf("a==b\n");
}
if (a != b)
{
printf("a!=b\n");
}
int c = 20;
int d = 30;
if (c >= d)
{
printf("c>=d\n");
}
if (c <= d)
{
printf("c<=d\n");
}
return 0;
}
逻辑操作符
&& 逻辑与
&& 并且操作符:只要一个条件为假,结果就为假,全部为真结果才为真
短路现象:当我们 && 多个条件时:若第一个条件为假,则结果直接为假,后面的条件不会对其进行判断(也就是只要遇到假条件,后面的所有条件都不会被判断)
#include <stdio.h>
int main()
{
// 输入一个年份
//判断是否 能被4整除 并且 不能被100整除
//若能 打印出这个年份
int year = 0;
scanf("%d", &year);
if ((year % 4 == 0) && (year % 100 != 0))
{
printf("此年份为:%d\n",year);
}
else
{
printf("%d 年份 (能被4整除并且不能被100整除)不成立\n", year);
}
return 0;
}
|| 逻辑或
|| 或者操作符: 只要有一个条件为真结果就为真,全都为假 结果才为假
短路现象:当我们判断的条件只要遇到条件为真,后面的条件就不判断
#include <stdio.h>
int main()
{
//判断闰年
//能被4整除并且不能被100整除,或者能被400整除的年份是闰年
int year = 0;
scanf("%d", &year);
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
printf("%d 年 是闰年\n",year);
}
else
{
printf("%d 年 不是闰年\n",year);
}
return 0;
}
! 逻辑非
! 逻辑反操作符
让真变为假,假变为真
#include <stdio.h>
int main()
{
// 在C语言中数字 0 表示假,非0数字表示真
int a = 10;
if (!a)
{
printf("!a 为真\n");
}
else
{
printf("!a 为假\n");
}
return 0;
}
条件操作符
表达式1 ? 表达式2 : 表达式3
表达式1 ?表示式2 :表达式3 ;
条件操作符,又叫三目运算符,因为有三个操作数
当表达式1 为真执行表达式2 ,否则执行表达式3
#include <stdio.h>
int main()
{
//输入两个数打印两个数中的较大值
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int max = (a > b ? a : b);
printf("%d\n", max);
//a > b ? printf("%d\n", a) : printf("%d\n", b);
return 0;
}
逗号表达式
表达式1,表达式2,……,表达式n
逗号表达式就是用逗号隔开多个表达式
逗号表达式从左向右依次执行
逗号表达式的结果就是最后一个表达式
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
//d的结果就是逗号表达式中最后一个表达式的结果
//也就是将c的值赋给d
int d = (a, b, c);
printf("%d\n", d); // d为30
}
下标引用操作符
[] 下标引用
[] 下标引用操作符 ,以索引的方式操作数组
[下标数]
#include <stdio.h>
int main()
{
int arr[5] = { 1,2,3,4,5 };
//下标引用
arr[0] = 8;
//打印数组
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
函数调用操作符
()
函数名():进行函数调用
()里面可以有参数,也可以没有参数
函数调用操作符,至少有一个操作数,函数名也是它的操作数,其余操作数就是所传的参数
#include <stdio.h>
void Print(int a)
{
printf("%d\n", a);
}
int main()
{
int a = 10;
Print(a);//() 有两个操作数 函数名和a
return 0;
}
结构体成员访问操作符
. 操作符
结构体变量 . 成员变量
用结构体变量访问结构体成员
#include <stdio.h>
//创建结构体
struct SS {
char name[10];
int age;
};
int main()
{
//创建结构体变量并初始化
struct SS s1 = { "张三",18 };
//访问结构体成员(结构体变量 . 成员变量)
printf("名字:%s 年龄:%d\n", s1.name, s1.age);
}
-> 操作符
结构体指针->结构体成员变量
用结构体指针访问成员
#include <stdio.h>
//创建结构体
struct SS {
char name[10];
int age;
};
int main()
{
//创建结构体变量并初始化
struct SS s1 = { "张三",18 };
//创建结构体指针变量 让其指向s1
struct SS* p = &s1;
// 用结构体指针访问成员(->)
printf("名字:%s 年龄:%d\n", p->name, p->age);
}
隐式类型转换
整型提升
在C语言中,当小于int 类型的 其它类型在计算的时候会将其 转化成int 类型进行计算
最后在进行截断赋给接收值的类型,若是int 则不截断
在整型提升过程中 不足整型的类型数 :
无符号数二进制位前面补0,有符号数二进制位前面补1 补够一个int 类型大小即可
#include <stdio.h>
int main()
{
short a = 10; //在内存中占两个字节
//二进制表示:00000000 00001010
char b = 1;//在内存中占一个字节
//二进制表示:00000001
short c = a + b;
//在计算的时候进行整型提升在计算
// 无符号数前面补0,有符号数补符号位
// a提升:00000000 00000000 00000000 00001010
// b提升:00000000 00000000 00000000 00000001
// a+b :00000000 00000000 00000000 00001011
//放进c中的时候因为c是short类型所以要发生截断
// c : 00000000 00001011
// c=11
printf("%d\n", c);
return 0;
}
算术转换
在进行计算时:如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型 这就是算术转换
long double
double
float
unsigned long int
long int
unsigned int
int
自下而上,进行算术转换
#include <stdio.h>
int main()
{
int a = 3;
double b = 3.14;
double c = a + b;
//此时会将整型变量a 算术转换成 double 类型 进行计算
printf("%lf\n", c);
return 0;
}
操作符的属性
1、操作符的优先级
多个相邻操作符混合计算时,我们就得考虑操作符的优先级
优先级高的先计算
2、操作符的结合性
有些操作符是具有结合性的既遵从从左向右计算,也遵从特殊的两个先结合计算
3、操作符是否控制求值顺序
有些操作符是控制求值顺序的
像,逗号表达式、三目运算符、逻辑与、逻辑或,都是控制求值顺序的,从左向右依次执行
看图参考: