【参考课程:B站 BV1cq4y1U7sg BV1Vm4y1r7jY】
目录
各类操作符
1.算数操作符
+ 加 - 减 * 乘 / 除 % 取模
除法操作符 /
int main()
{
//除法
//整数除法 //计算除法后只保留整数位-[不四舍五入]直接去除小数位
int a = 7 / 4; //a值为1
float b = 7 / 4; //b值为1.00000000 //执行整数除法-与结果值的储存类型无关
//浮点数除法 -只要‘/’两端有任意数为浮点数,即执行浮点数除法
int c = 7.0 / 4; //c值为1 //虽然进行了浮点数除法,但c的类型为整型
float d = 7.0 / 4; //d值为1.75000000
return 0;
}
取模操作符 %
//取模操作符的两个操作数必须为整数,返回的是整除之后的余数
int main()
{
int a = 7 % 3; //7除3等于2,余数为1 //a值为1
return 0;
}
2.移位操作符
//移动的是二进制位
<< 左移操作符 //左边丢弃,右边补0
>> 右移操作符 //算数右移\逻辑右移 --通常采用算数右移
右移操作符 >>
#include<stdio.h>
int main()
{
int a = -1;
int b = a << 1; //左移 //-2
int c = a >> 1; //右移 //-1
printf("b=%d\nc=%d\n", b,c);
return 0;
}
整数的三种二进制表示形式:
- 原码:直接根据数值写出的二进制序列
- 反码:原码的符号位(左一位)不变,其他位按位取反
- 补码:反码+1
以上针对的是负数 //对正整数原\反\补相同
左移操作符 <<
#include<stdio.h>
int main()
{
int a = 2;
//把a的二进制位向左移动一位
int b = a << 1;
printf("b=%d\n", b); //输出 b=4
return 0;
}
3.位操作符
//位操作作用对象只能为整数
& 按位与
| 按位或
^ 按位异或
按位与 &
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
//& -按(二进制)位与
int c = a & b;
//a:00000000000000000000000000000011
//b:00000000000000000000000000000101
//按位与:对应二进制位只要有0则与为0;全是1则与为1
//c:00000000000000000000000000000001
printf("c=%d\n", c); //输出 c=1
return 0;
}
按位或 |
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
//| -按(二进制)位或
int c = a | b;
//a:00000000000000000000000000000011
//b:00000000000000000000000000000101
//按位或:对应二进制位只要有1则与为1;全是0则与为0
//c:00000000000000000000000000000111
printf("c=%d\n", c); //输出 c=7
return 0;
}
按位异或 ^
//相同为0,相异为1
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
//^ -按(二进制)位异或
int c = a ^ b;
//a:00000000000000000000000000000011
//b:00000000000000000000000000000101
//按位异或:对应二进制位相同为0,相异为1
//c:00000000000000000000000000000110
printf("c=%d\n", c); //输出 c=6
return 0;
}
【按位异或^ 应用】 写程序交换a、b的值。要求不引入新的变量
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 0; int b = 0; scanf("%d %d",&a,&b); printf("交换前\na=%d b=%d\n",a,b); //交换a、b的值 a = a ^ b; b = a ^ b; a = a ^ b; printf("交换后\na=%d b=%d\n", a, b); return 0; }
思路: a ^ a = 0; 0 ^ a = a; ∴ a ^ b ^ b = a ^ 0 = a;
【练习】编写代码实现:求一个整数储存在内存中的二进制中1的个数
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Count(int num)
{
int i = 0; //计循环次数
int c = 0; //计1的个数
for (i = 0; i < 32; i++,num=num>>1)
{
if (num & 1 == 1)
{
c++;
}
}
return c;
}
int main()
{
int n = 0;
scanf("%d",&n);
int Count(int num);
printf("%d", Count(n));
return 0;
}
4.赋值操作符
= 直接赋值操作符
+= -= *= /= %= >>= <<= &= |= ^= 复合赋值操作符
直接赋值操作符
//从右向左赋值→左值为可变值//左值不能是计算式
int main()
{
//支持连续赋值
int a = 1;
int b = 2;
int x = 0;
x = a = b + 1;
//从右向左赋值
return 0;
}
复合赋值操作符
int main()
{
int a = 0;
//两种等价写法:
a = a + 100;
a += 100;
return 0;
}
5.单目操作符
单目操作符---只有一个操作数的操作符
! 逻辑反操作
//转换逻辑上的真假
#include<stdio.h>
int main()
{
int eg = 5;
if (eg) //如果eg为真打印“二狗”
printf("二狗\n");
if (!eg) //如果eg为假打印“ago”
printf("ago\n");
return 0;
}
+ - 正负操作符
int main()
{
int a = 5;
a = -a; //a变为 -5
return 0;
}
sizeof 计算操作数的类型长度(单位:字节)
#include<stdio.h>
int main()
{
int a = 0;
char b = 0;
int arr[10] = { 0 };
//sizeof 可计算操作数的类型长度
printf("%d\n", sizeof(a)); //4
printf("%d\n", sizeof(b)); //1
//带变量可省略()--sizeof是操作符不是函数
printf("%d\n", sizeof a); //4
//带数据类型不可省略()
printf("%d\n", sizeof(int)); //4
//sizeof 可计算数组大小
printf("%d\n", sizeof(arr)); //40
//也可计算数组类型 //*去掉数组名剩下的就是数组类型
printf("%d\n", sizeof(int [10])); //40
//也可计算数组单个元素大小
printf("%d\n", sizeof(arr[0])); //4
//∴常用sizeof计算数组元素个数
int sz = 0; //sz为元素个数
sz = sizeof(arr) / sizeof(arr[0]); //10
return 0;
}
#include<stdio.h>
int main()
{
int a = 10;
char b = 7;
short c = 6;
printf("%d\n", sizeof(c = a + 2)); //2
//此处是把 a+2 的值赋给 c ;打印 c 的类型长度
//∴本质是打印 c 的类型长度, c 的类型是short,占2字节 //输出2
printf("%d\n", c); //6
//sizeof 内部的表达式不参与运算
//∴ c = a + 2 只在操作符 sizeof 下进行,c 的值并没有改变 //输出6
return 0;
}
~ 对一个数的二进制位按位取反(包括符号位)
#include<stdio.h>
int main()
{
int a = -1;
//原码:10000000000000000000000000000001
//反码:11111111111111111111111111111110
//补码:11111111111111111111111111111111 ⬅负数储存补码
int b = ~a; //~ 按位取反(包括符号位)
//a补码:11111111111111111111111111111111
//~a: 00000000000000000000000000000000
printf("%d\n", b); //0
return 0;
}
[前置\后置]++ - - 自增 自减
前置++\- -:先加加\减减,再使用
后置++\- -:先使用,再加加\减减
#include<stdio.h>
int main()
{
//前置++ //先++,后赋值
int a = 10;
int b = ++a;
printf("%d\n", a); //先++→a+1=11//输出11
printf("%d\n", b); //后使用→b=a=11//输出11
//后置++ //先赋值,后++
int c = 10;
int d = c++;
printf("%d\n", d); //先赋值→d=c=10 //输出10
printf("%d\n", c); //后++→c+1=11 //输出11
return 0;
}
取地址操作符 & 解引用操作符 *
//取地址操作符&与位操作符&区分方法:看有几个操作数
#include<stdio.h>
int main()
{
int a = 10;
//& -取地址操作符
//%p -打印十六进制表示的地址
printf("%p\n", &a); //00000064D917F724 (a的地址)
//创建指针 pa 用于存放a的地址 -pa的类型为 int*
int* pa = &a; //这里*表示pa是指针变量,而非解引用操作符
//* -解引用操作符\间接访问操作符
//通过访问储存在pa里的a的地址来间接访问a,并对a的值进行操作(赋值为20)
*pa = 20;
printf("%d\n", a); //20
return 0;
}
(类型) 强制类型转换
int main()
{
int a = (int)3.14;
return 0;
}
6.关系操作符
> < >= <= == !=
!!!比较两个字符串是否一致不能使用 ==
*注意区分关系操作符 == 与赋值操作符 =
7.逻辑操作符
&& 逻辑与
| | 逻辑或
//只看逻辑真假 //注意与位操作符区分
int main()
{
int a=3,b = 0, c = 1;
int m = a && c; //m=1 -非0即值为1
int n = a && b; //n=0
return 0;
}
【例题1】问执行代码输出的是什么
#include<stdio.h>
int main()
{
int x = 0;
int a = 0, b = 2, c = 3, d = 4;
x = a++ && ++b && d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
【例题2】问执行代码输出的是什么
#include<stdio.h>
int main()
{
int y = 0;
int a = 0, b = 2, c = 3, d = 4;
y = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
分析:
| | 与 && 不同,不存在遇 0 跳出,所以最终 a、b、c、d 的值分别为 1、3、3、4
8.条件操作符//三目操作符
//唯一有三个操作数的操作符
(表达式1 ? 表达式2 : 表达式3)
//若表达式1为真--计算表达式2
//若表达式1为假--计算表达式3
int main()
{
int a = 0;
int b = 6;
a = (b > 3 ? 1 : -1); //a值为1
return 0;
}
9.逗号表达式
(表达式1, 表达式2, ... , 表达式n)
//从左向右依次计算,但整个表达式的值为最后一个表达式的值
int main()
{
int a = 1;
int b = 2;
int c = 3;
int n = (c = 5, a = c + 3, b = a - 4, c += 5);
//a = 8 , b = 4 , c = 10 , n = 10
return 0;
}
10.下标引用、函数调用和结构成员
下标引用操作符 [ ]
//有两个操作数:数组 与 元素下标
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; //此处[]为语法格式
//数组下标从0开始
printf("%d\n", arr[4]); //此处[]为下标引用操作符
//引用数组arr下标为4的元素
return 0;
}
函数调用操作符( )
int ADD(int x, int y)
{
return x + y;
}
void test()
{}
int main()
{
int a = 6, b = 11;
int ret = ADD(a, b);//此处()为函数调用操作符,不可省略
test();//即使函数不传参,调用时也不能省略()
return 0;
}
结构成员访问操作符 . ->
#include<stdio.h>
//创建自定义结构体类型:Book
struct Book
{
//name、id、price -结构体成员变量
char name[30];
char id[20];
int price;
}; //注意定义结构体的{}外要加;
int main()
{
//初始化一个Book类型的结构体b
struct Book b = { "C语言从入门到飞天","C20230722",66 };
//用结构成员访问操作符'.'访问成员
//结构体变量名.成员名
printf("书名:%s\n",b.name);
printf("ID:%s\n", b.id);
printf("价格:%d\n", b.price);
//用结构成员访问操作符'->'通过访问结构体地址来访问成员
//结构体指针->成员名
struct Book* pb = &b;
printf("书名:%s\n", pb->name);
printf("ID:%s\n", pb->id);
printf("价格:%d\n", pb->price);
return 0;
}
表达式求值
表达式的求值顺序有三个影响因素:
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序
//如果表达式不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的!
————————————————
c语言运算符优先级
有些表达式的操作数在求值的过程中可能需要转换为其他类型:
1.隐式类型转换
整型提升
整型提升Integral Promotion是指在计算表达式中,小于int类型的整数类型(如char、short、unsigned char等)被自动提升为更大的整数类型(如int或unsigned int),以便进行操作符的运算。
整型提升发生在以下情况下:
- 当一个表达式中同时包含不同的整数类型时,会将其中较小的类型提升为较大的类型,以便进行操作。
- 当使用特定的操作符(例如算术操作符、位操作符、关系操作符等)对小于int类型的值进行操作时,会将这些值提升为int类型,然后再执行操作。
整型提升主要用于确保在表达式中的不同整数类型之间进行一致的运算,并避免精度丢失和数据溢出的问题。通过提升为较大的整数类型,可以确保结果能够容纳所有可能的值,并保持表达式的一致性。
【整型提升示例1】
#include<stdio.h> int main() { char a = 3; char b = 127; char c = a + b; printf("%d\n", c); //输出-126 return 0; }
【整型提升示例2】
#include<stdio.h> int main() { char a = 0; printf("%u\n", sizeof a); //1 printf("%u\n", sizeof (+a)); //4 //发生整型提升 printf("%u\n", sizeof (-a)); //4 //发生整型提升 return 0; }
//计算式具有两个属性:①值属性 ②类型属性
sizeof对计算式操作时,虽然其中运算并不进行,但最终输出的是计算式类型属性的内存大小
算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数类型,否则操作无法进行
//算数转换是向上转换的:
long double double //⬆ float //⬆ unsigned long int //⬆ long int //⬆ unsigned int //⬆ int //⬆
*向上转换--向精度更高、长度更长的去转换
2.显式类型转换
//强制类型转换操作符