C++学习之路【1】:标C数据结构和算法
运算符
运算符是代表数字处理规则的符号
根据运算符所需要配合的数字个数把运算符分为:单目运算符、双目运算符和三目运算符
5 + 6 此时的 + 就是双目运算符
算数运算符
双目算数运算符
常用算数运算符(+ - * / %)
c语言中用+,-,*,和 / 表示加减乘除四则运算符号,它们都是双目操作符
c语言里如果参与除法计算的两个数字都是整数则计算结果只保留整数部分:7 / 3 = 2 ; 5 / 2 = 2
如果想要得到完整的结果:5 / 2.0 = 2.5
c语言中用 % 表示取余操作,不可以对小数做取余操作:5 % 2 = 1
#include<stdio.h>
int main(void){
pritf("5 / 2 = %d\n", 5 / 2);//2
pritf("5 / 2.0 = %lg\n", 5 / 2.0);//2.5
pritf("5 %% 2 = %d\n", 5 % 2);//1
return 0;
}
应用:根据输入的秒数划算成时分秒格式显示
赋值运算符( = )
c语言里用 = 表示赋值运算符: 存储区 = 数据
赋值运算符的功能就是将右边的数据放到符号左边的存储区中
可以在一条语句里使用多个赋值操作符,这个时候优先计算右边的操作符:a = b = c; // b = c, a = b
复合运算符(+= -= *= /=)
c语言里绝大多数【双目操作符】可以和赋值操作符合并称为复合赋值操作符,例如: +=, /=, -=, *= 等
复合赋值操作符左边的内容也应该代表存储区,右边的内容也应该代表数字:
左边 = 存储区(变量)
右边 = 数字
int val = 0;
val += 300;
复合赋值操作符会把【双目操作符的计算结果】记录到左边的存储区里
val += 300;
val + 300 的结果为 300
val = 300 // 将val + 300运算的结果赋值给左边val表示的存储区
val += 300;等价于 val = val + 300;
int a = 100;
a *= 2 + 1; // a = a * (2 + 1)
printf("a = %d\n", a); //输出300
单目算数运算符
自增操作符(++)和自减操作符(- -)
它们只能和存储区配合使用
它们可以把存储区里的内容加一或者减一(不能对常数自增自减)
它们有两种使用方法:
一种叫做前操作(操作写在存储区的前面)
++val - 将val表示的存储区的值加1
一种是后操作(操作符写在存储区的后面)
val++ - 将val表示的存储区的值加1
...
int a = 100;
++a;
printf("a = %d\n", a); // 101
a++;
printf("a = %d\n", a); // 102
--a;
printf("a = %d\n", a); // 101
a--;
printf("a = %d\n", a); // 100
...
自增和自减操作符编写的表达式当做数字使用的时候前操作和后操作结果不同:
前操作表达式当做数字使用的时候是修改后的数字
val = 10;
int val = ++val;// 11
后操作表达式当做数字使用的时候是修改前的数字
val = 10;
int val = val++; // 10
不要在一个表达式里对同一变量多次进行自增或自减计算
...
int a = 0, b = 0;
b = 10;
a = ++b;
printf("a = %d, b = %d\n", a, b); // 11 11
b = 10;
a = b++;
printf("a = %d, b = %d\n", a, b); // 10 11
逻辑运算符
逻辑运算符用来编写逻辑表达式
逻辑表达式的结果一定是布尔值:
0 - 假 - 不成立
1 - 真 - 成立
单目逻辑运算符( ! )
! 是一个单目逻辑运算符,它可以根据一个布尔值计算出相反的布尔值,这个操作叫做逻辑取反:!10为0(假);!0为1(真)
双目逻辑运算符(== > < && || 等)
双目逻辑运算符包括:
等于( == ),不等于( != )
大于( > ),大于等于( >= )
小于( < ),小于等于( <= )
如果一个逻辑表达式里最多包含一个双目逻辑运算符则它的计算结果在数学里和C语言里一定相同,这种表达式叫简单逻辑表达式
如果一个逻辑表达式里包含多个双目逻辑操作符就必须拆分成多个简单逻辑表达式然后再合并
5 < 4 < 3 - 1 (从左到右,一次只会计算一个逻辑表达式。尽量不要这样写)
5 < 4 - 0
0 < 3 - 1
与(&&)和或(||)也都是双目逻辑操作符,它们可以用来把两个逻辑表达式合并成一个
C = A && B
C = A || B
如果两个逻辑表达式的结果都是真则用与(&&)连接后结果也是真,否则结果为假:输入用户名+密码 -> 用户名(真)+密码(真)->登录成功
如果两个逻辑表达式里有一个的结果是真则用或( || )连接后结果就是真,否则结果是假:验证码或者密码登录 -> 有一个成功 -> 成功
#include<stdio.h>
int main(void){
printf("与运算(&&)+或运算(||):\n");
printf("5 && (6 < 9) = %d\n", 5 && (6 < 9)); //1
printf("5 && (6 > 9) = %d\n", 5 && (6 > 9)); //0
printf("5 && 0 = %d\n", 5 && 0 ); //0
printf("5 || (6 < 9) = %d\n", 5 || (6 < 9)); //1
printf("5 || (6 > 9) = %d\n", 5 || (6 > 9)); //1
printf("5 || 0 = %d\n", 5 || 0 ); //1
printf("与运算(&&)+或运算(||)短路运算:\n");
printf("0 && xxx = %d\n", 0 && xxx ); //0
printf("1 || xxx = %d\n", 1 || xxx ); //1
return 0;
}
与(&&)和或(||)都具有短路特征(如果前一个逻辑表达式的结果可以决定合并后逻辑表达式的结果计算机就忽略后一个逻辑表达式)
int a = 100;
1 || ++a;
printf("a = %d\n", a); //100
int a = 100;
0 && ++a;
printf("a = %d\n", a); //100
短路与:若第一个表达式为假,则结果为假,后面的表达式不再计算
短路或:若第一个表达式为真,则结果为真,后面的表达式不再计算
位运算符
位运算符可以直接操作二进制数位的内容
位运算符 - 首先将数据转换成二进制
单目位运算符(~)
~ 是一个单目位运算符,它可以根据一个数字计算出另一个数字,这两个数字所有二进制数位的内容都不一样,这个操作叫做按位取反
#include<stdio.h>
int main(void){
printf("按位取反(~)- 有陷阱:\n");
printf("3 & 5 = %d\n", 3 & 5); //1
// 0101 1010 0x5a
//-------------~
// 1010 0101 0xa5
// 0x5a - 数据类型 - int类型
// 0x5a - 00000000 00000000 00000000 01011010 - 完整
//-------------------------------------------
// 11111111 11111111 11111111 10100101
// f f f f f f a 5
// 0xffffffa5
//按位取反 - 数据类型
printf("~0x5a = %#x\n", ~0x5a); //0xffffffa5
printf("~0x5a = %#hhx\n", ~0x5a);
return 0;
}
双目位运算符(& | ^ >> <<)
双目位运算符包括按位与(&),按位或( | )和按位异或(^)
双目位运算符把两个数字对应二进制数位的内容相互进行计算
按位与(&)可以把对应数位的内容做与计算
-
3 0000 0011
-
& 5 0000 0101
-
0000 0001
任何数位内容和1做按位与结果保持不变
0 & 1 = 0
1 & 1 = 1
任何数位内容和0做按位与结果就是0
1 & 0 = 0
0 & 0 = 0
#include<stdio.h>
int main(void){
printf("\与运算(&):\n");
printf("3 & 5 = %d\n", 3 & 5); //1
// 0100 0100 0x44
// 1100 0001 0xc1
// ------------&
// 0100 0000 0x40
printf("0x44 & 0xc1 = %#x\n", 0x44 & 0xc1); //0x40
return 0;
}
按位或( | )可以把两个数字对应的内容做或计算
任何数位内容和0做按位或结果不变:
1 | 0 = 1
0 | 0 = 0
任何数位内容和1做按位或结果是1:
1 | 1 = 1
0 | 1 = 1
使用按位或可以把一个数字的某些二进制内容设置成1
...
printf("\或运算(|):\n");
printf("3 | 5 = %d\n", 3 | 5); //7
// 0100 0100 0x44
// 1100 0001 0xc1
// ------------|
// 1100 0101 0xc5
printf("0x44 | 0xc1 = %#x\n", 0x44 | 0xc1); //0xc5
...
按位异或( ^ )可以把两个数字对应数位的内容做异或计算
如果两个数位的内容一样则异或计算后结果是0:
0 ^ 0 = 0
1 ^ 1 = 1
如果两个数位的内容不一样则异或计算后结果是1:
0 ^ 1 = 1
1 ^ 0 = 1
...
printf("\或运算(^):\n");
printf("3 | 5 = %d\n", 3 | 5); //7
// 0100 0100 0x44
// 1100 0001 0xc1
// ------------^
// 1000 0101 0x85
printf("0x44 ^ 0xc1 = %#x\n", 0x44 ^ 0xc1); //0x85
...
移位操作可以把一个数字里所有二进制数位的内容统一向左或者向右移动n个位置:右移使用>>表示;左移使用<<表示,它们都是双目操作符
操作符左边(右边)的数字是将要移动的位数
a << n 意思就是将变量a左移n位
移位操作相当于把每个数位的内容放到你一个数位里
左移位(<<):
有符号数,右补0
10101010(-86) << 1 = 01010100(84)
将高位移出的数据丢弃,低位填充0
无符号数,右补0
10101010(170) << 1 = 01010100(84)
将高位移出的数据丢弃,低位填充0
右移位(>>):
有符号数,左补符号位
10101010(-86) >> 1 = 11010101(-43)
将低位移出的数据丢弃,高位填充符号位
无符号数,左补0
10101010(170) >> 1 = 01010101(85)
将低位移出的数据丢弃,高位填充0
...
char a = 0x5a; //01011010
char b = a <<2;
short c = a <<2;
/* a << 2 a的值是否变化 - 没有 -(此时将变量a的值拿出来,左移两位,将移位之后的记住赋值给b)
a(0x5a) ---(0x5a)---> cpu(左移两位) ---> b
7 6 5 4 3 2 1 0 位编号
| | | | | | | | | 字节
0 1 0 1 1 0 1 0 0 0 将该二进制的值赋值给变量b
char b = 0 1 0 1 1 0 1 0 0 0
变量b(char)的存储只有8bit,将高位丢弃,只保留低8位
0 1 0 1 1 0 1 0 0 0 将该二进制的值赋值给变量c
short c = 0 1 0 1 1 0 1 0 0 0
变量c(short)的存储有16bit,不会有数据丢失,高位补零
*/
printf("a = %#x, b = %#x, c = %#x\n", a, b, c);//0x5a 0x68 0x168
b = a >> 2;// 01011010 >> 2 = 00010110 = 0x16
printf("a = %#x, b = %#x\n", a, b);//0x5a 0x16
在不发生高位溢出的前提下,左移1(n)位相当于乘以2(2的n次方):
00001111(15)<< 1 = 00011110(30)
左移1(n)位相当于乘除以2(2的n次方):
00001111(15)>> 1 = 00000111(7)
位操作符不会修改存储区内容
三目运算符
三目操作符可以从两个不同的计算规则里选择一个进行计算
三目操作符格式:布尔值 ?表达式一 :表达式二
如果布尔值为真就用表达式一计算结果,如果布尔值为假就用表达式二计算结果
不要在问号后使用赋值操作符
#include<stdio.h>
int main(void){
int a = 0;
printf("input a integer:");
scanf("%d", &a);
printf("\n获取变量a的绝对值:\n");
a = (a >= 0) ? a : (0 - a);
printf("变量a的绝对值为%d\n:", a);
return 0;
}
运算符优先级
单目高于双目
乘除高于加减
算术高于关系高于逻辑
条件高于赋值高于逗号
优先级数字越大优先级越高,搞不清楚优先级可以加括号()
多数运算符具有左结合序:
a - b + c 等价于 (a -b)+ c
单目、三目和赋值运算符具有右结合序
-----a 等价于 -(–(–a))
a > b ? a : c > d ? c : d 等价于 a > b ? a : (c > d ? c : d)
a = b+= c 等价于 a = (b+=c)
类型转换运算
如果一个表达式里包含多个不同类型的数字则计算机会首先把它们转换成同一个类型然后在进行计算。这个转换过程叫隐式类型转换,这个转换完全由计算机完成。
隐式类型转换过程中一定把占地小的类型转换成占地大的类型
如果不同类型数字占地大小一样就把整数类型转换成浮点数类型,把有符号类型转换成无符号类型
#include<stdio.h>
int main(viod){
//4
//1 - int
//0.9 - double
//1.0 ? 1.0 : 0.9
//double
//8
printf("sizeof(1 ? 1 : 0.9) = %lu\n", sizeof(1 ? 1 : 0.9));//8
printf("(-7 + 3) > 0 = %d\n", (-7 + 3) > 0);//0
// -7, 0 -int
//3u - unsigned int - 3
// -7转换为其无符号类型 - 非负数
printf("(-7 + 3u) > 0 = %d\n", (-7 + 3u) > 0);//1
return 0;
}
C语言程序里可以临时给数字指定一个类型,这叫强制类型转换;强制类型转换有可能导致数据内容丢失;类型转换不会修改存储区内容
...
int a = 300;
char c = (char)a;
printf("a = %d, c = %hhd\n", a, c); //300 44
数据300的二进制内容:
将变量a存储区的值强制转换成char类型,即只获得一个字节的内容(绿色部分),此时发生了数据丢失
常用于指针