无锡市新区建设环保局网站,信息作业网站下载,北京住房和城乡建设官方网站,云南省建设厅合同网站文章目录1.算术操作符2.移位操作符2.1 左移操作符2.2 右移操作符3.位操作符按位与按位或按位异或4.赋值操作符复合赋值符5.单目操作符5.1单目操作符介绍6.关系操作符7.逻辑操作符8.条件操作符9.逗号表达式10.下标引用、函数调用和结构成员11表达式求值11.1 隐式类型转换11.2算术…
文章目录1.算术操作符2.移位操作符2.1 左移操作符2.2 右移操作符3.位操作符按位与按位或按位异或4.赋值操作符复合赋值符5.单目操作符5.1单目操作符介绍6.关系操作符7.逻辑操作符8.条件操作符9.逗号表达式10.下标引用、函数调用和结构成员11表达式求值11.1 隐式类型转换11.2算术转换11.3操作符的属性操作符分类 算术操作符移位操作符位操作符赋值操作符单目操作符关系操作符逻辑操作符条件操作符逗号表达式下标引用、函数调用和结构成员 1.算术操作符 - * / %除了 % 操作符之外其他的几个操作符可以作用于整数和浮点数。对于 / 操作符如果两个操作数都为整数执行整数除法。而只要有浮点数执行的就是浮点数除法。% 操作符的两个操作数必须为整数。返回的是整除之后的余数。/ 得到的是商% 得到的是余数
2.移位操作符 左移操作符右移操作符注移位操作符的操作数只能是整数移位操作符移动的是二进制的位我们日常中的数字都是十进制十进制向二进制的转化如下所示 二进制每一位都有他自己的权重从右到左依次是2的0次方2的1次方2的3次方···· 整数的二进制表现形式是什么样的呢 二进制的表现形式有三种原码反码补码 原码把一个数按照正负直接翻译成二进制就是原码 反码反码的符号位不变其他位按位取反就是反码 补码反码1 注 正整数的原码、反码、补码都是相同的 负整数的原码、反码、补码是需要计算的 以-5为例 最高的一位表示符号位0是正数1是负数 整数在内存中存储的是补码的二进制序列 2.1 左移操作符
移位规则 左边抛弃、右边补0 看代码
#include stdio.h
int main()
{int a 3;int b a 1;printf(%d\n, b);printf(%d\n, a);return 0;
}3的二进制位为 00000000000000000000000000000011 十进制转二进制方法十进制的数除以要转制的基数二进制就是2取其余数由下向上写结果前边剩余位置补0 如图所示 左移之后产生的结果为 00000000000000000000000000000110 打印结果为6 若a-3怎么求移动之后的数呢 a-3时a的二进制位为原码为 10000000000000000000000000000011 补码为因为二进制中储存移动的时补码 11111111111111111111111111111101 移动之后为 11111111111111111111111111111010 但是求移动后的数是由原码得到的所以移动后原码为 10000000000000000000000000000110 运行结果为-6 原码与补码的转换
2.2 右移操作符
移位规则 首先右移运算分两种 逻辑移位 左边用0填充右边丢弃算术移位 左边用原该值的符号位填充右边丢弃 右移的时候采用哪种方法是取决于编译器的 看代码
#include stdio.h
int main()
{int a -1;int b a 1;printf(b %d\n, b);printf(a %d\n, a);return 0;
}-1在内存中存储的补码为 11111111111111111111111111111111 运行结果 我们发现在vs2022运行下结果为-1所以编译器采用的是算数右移绝大多数编译器都是算数右移 警告⚠ 对于移位运算符不要移动负数位这个是标准未定义的。 例如
int num 10;
num-1;//error3.位操作符 //按位与| //按位或^ //按位异或注他们的操作数必须是整数。按位与
看代码
#include stdio.h
int main()
{int a 3;int b -5;int c a b;printf(%d\n, c);return 0;}00000000000000000000000000000011 - 3的补码 11111111111111111111111111111011 -5的补码 按位与是对应的二进制位进行按位与1和0取0同0取0同1取1 按位与之后结果 00000000000000000000000000000011 所以按位与结果为3
按位或
看代码
#include stdio.h
int main()
{int a 3;int b -5;int c a | b;printf(%d\n, c);return 0;}按位或也是对应的二进制位进行按位或1或0之间取1同0为0同1为1 按位或之后结果 11111111111111111111111111111011 所以结果为-5
按位异或
看代码
#include stdio.h
int main()
{int a 3;int b -5;int c a ^ b;printf(%d\n, c);return 0;}按位异或 -对应的二进制位相同为0相异为1 按位异或结果为 11111111111111111111111111111000 取原码之后打印结果为-8
例题 不能创建临时变量第三个变量实现两个数的交换。 #include stdio.h
int main()
{int a 10;int b 20;a a ^ b;b a ^ b;a a ^ b;printf(a %d b %d\n, a, b);return 0;
}a^a 0
0^a a
10^ 10 ^20 20
10^ 20 ^10 20
异或是支持交换律的4.赋值操作符
他可以让你改变一个你之前不满意的值。也就是你可以给自己重新赋值 例如
int weight 120;//体重
weight 89;//不满意就赋值
double salary 10000.0;
salary 20000.0;//使用赋值操作符赋值。赋值操作符可以连续使用比如
int a 10;
int x 0;
int y 20;
a x y1;//连续赋值
这样的代码感觉怎么样
那同样的语义你看看
x y1;
a x;
这样的写法是不是更加清晰爽朗而且易于调试。复合赋值符 -*/%|^这些运算符都可以写成复合的效果。 比如
int x 10;
x x10;
x 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁。5.单目操作符
5.1单目操作符介绍 ! 逻辑反操作- 负值 正值 取地址sizeof 操作数的类型长度以字节为单位~ 对一个数的二进制按位取反-- 前置、后置-- 前置、后置* 间接访问操作符(解引用操作符)(类型) 强制类型转换代码演示
#include stdio.h
int main()
{int a 10;int* p a;//取地址操作符*p 20;//解引用操作符(间接访问操作符)return 0;
}
解引用和取地址一般是连在一起使用的
#include stdio.h
int main()
{int a 10;printf(%d\n, sizeof a);printf(%d\n, sizeof(a));printf(%d\n, sizeof(int));//printf(%d\n, sizeof int);不能省略括号return 0;}运行结果 数组的使用方法也一样 如图所示
#include stdio.h
int main()
{int arr[10] { 0 };printf(%d\n, sizeof(arr));printf(%d\n, sizeof(arr[0]));int sz sizeof(arr) / sizeof(arr[0]);//计算数组大小printf(%d\n, sz);return 0;
}运行结果 int a 10;int b a;//后置先使用再//int b a,aa1;运行结果为1110 int a 10;int b a;//前置先再使用//aa1int b a;运行结果为1111 - - 操作符和的使用方法一样
6.关系操作符
关系操作符种类 ! 用于测试“不相等” 用于测试“相等”注意 在编程的过程中 和不小心写错导致的错误。
7.逻辑操作符
逻辑操作符有哪些 逻辑与并且|| 逻辑或或者区分逻辑与逻辑或与按位与按位或逻辑与逻辑或只关注真假结果为1/0按位与按位或是二进制的计算。 看代码 逻辑与
#include stdio.h
int main()
{int a 3 0;//1 0printf(%d\n, a);return 0;运行结果 逻辑或 #include stdio.h
int main()
{int a 3 ||0;//1 0printf(%d\n, a);return 0;
}运行结果为1 练习
#include stdio.h
int main()
{int i 0, a 0, b 2, c 3, d 4;i a b d;//i a||b||d;printf( a %d\n b %d\n c %d\n d %d\n, a, b, c, d);return 0;
}逻辑与结果 逻辑或结果 总结 逻辑与第一个表达式为假不进行后边的计算 逻辑或第一个表达式为真不进行后边的计算
8.条件操作符
exp1 ? exp2 : exp3如果表达式1为真表达式2的结果是整个表达式的结果 如果表达式1为假表达式3的结果为整个表达式的结果 看代码
#include stdio.hint main()
{int a 0;int b 0;scanf(%d, a);b ((a 5) ? 3 : -3);printf(%d\n, b);return 0;
}9.逗号表达式
exp1, exp2, exp3, …expN逗号表达式就是用逗号隔开的多个表达式。 逗号表达式从左向右依次执行。整个表达式的结果是最后一个表达式的结果。 代码演示
#include stdio.h
int main()
{int a 1;int b 2;int c (a b, a b 10, a, b a 1);//逗号表达式printf(%d\n, c);return 0;
}运行结果为13
10.下标引用、函数调用和结构成员
[ ] 下标引用操作符 操作数一个数组名 一个索引值
int arr[10];//创建数组
arr[9] 10;//实用下标引用操作符。
[ ]的两个操作数是arr和9。( ) 函数调用操作符 接受一个或者多个操作数第一个操作数是函数名剩余的操作数就是传递给函数的参数。
#include stdio.h
void test1()
{printf(hehe\n);
}
void test2(const char* str)
{printf(%s\n, str);
}
int main()
{test1(); //实用作为函数调用操作符。test2(hello bit.);//实用作为函数调用操作符。return 0;
}访问一个结构的成员 . 结构体.成员名 - 结构体指针-成员名 代码演示
#include stdio.h
struct S
{int num;char c;
};void test(struct S* ps)
{//- 结构成员访问操作符//结构体指针-结构体成员printf(%d\n, ps-num);printf(%c\n, ps-c);
}
int main()
{struct S s {100, b};//结构体的初始化使用{}//打印结构中的成员数据//printf(%d\n, s.num);//printf(%c\n, s.c);//. 操作符 结构体变量.结构体成员名test(s);return 0;
}11表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。 同样有些表达式的操作数在求值的过程中可能需要转换为其他类型。
11.1 隐式类型转换
C的整型算术运算总是至少以缺省整型类型int的精度来进行的。 为了获得这个精度表达式中的字符和短整型操作数在使用之前被转换为普通整型这种转换称为整型 提升. 整型提升的意义 表达式的整型运算要在CPU的相应运算器件内执行CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度同时也是CPU的通用寄存器的长度。 因此即使两个char类型的相加在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。 通用CPUgeneral-purpose CPU是难以直接实现两个8比特字节直接相加运算虽然机器指令中可能有这种字节相加指令。所以表达式中各种长度可能小于int长度的整型值都必须先转换为int或unsigned int然后才能送入CPU去执行运算 //实例1
char a,b,c;
...
a b c;b和c的值被提升为普通整型然后再执行加法运算。 加法运算完成之后结果将被截断然后再存储于a中。 如何进行整体提升呢 整形提升是按照变量的数据类型的符号位来提升的 //负数的整形提升
char c1 -1;
变量c1的二进制位(补码)中只有8个比特位
1111111
因为 char 为有符号的 char
所以整形提升的时候高位补充符号位即为1
提升之后的结果是
11111111111111111111111111111111
//正数的整形提升
char c2 1;
变量c2的二进制位(补码)中只有8个比特位
00000001
因为 char 为有符号的 char
所以整形提升的时候高位补充符号位即为0
提升之后的结果是
00000000000000000000000000000001
//无符号整形提升高位补0代码演示
#include stdio.h
int main()
{char a 3;//00000000000000000000000000000011//00000011-截断char b 127;//00000000000000000000000001111111//01111111-截断char c a b;//00000000000000000000000000000011//00000000000000000000000001111111//00000000000000000000000010000010//10000010 - c//整型提升printf(%d\n, c);//11111111111111111111111110000010//11111111111111111111111110000001//10000000000000000000000001111110//-126return 0;
}整形提升的例子
//实例1
#include stdio.h
int main()
{//char -128~127char a 0xb6;short b 0xb600;int c 0xb6000000;if (a 0xb6)printf(a);if (b 0xb600)printf(b);if (c 0xb6000000)printf(c);return 0;
}
实例1中的a,b要进行整形提升,但是c不需要整形提升 a,b整形提升之后,变成了负数,所以表达式 a0xb6 , b0xb600 的结果是假,但是c不发生整形提升,则表达式 c0xb6000000 的结果是真。 所程序输出的结果是: c //实例2
#include stdio.h
int main()
{char c 1;printf(%u\n, sizeof(c));printf(%u\n, sizeof(c));printf(%u\n, sizeof(-c));return 0;
}实例2中的,c只要参与表达式运算,就会发生整形提升,表达式 c ,就会发生提升,所以 sizeof(c) 是4个字节.表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof© ,就是1个字节.
11.2算术转换
如果某个操作符的各个操作数属于不同的类型那么除非其中一个操作数的转换为另一个操作数的类型否则操作就无法进行。下面的层次体系称为 寻常算术转换。 long double double float unsigned long int long int unsigned int int 如果某个操作数的类型在上面这个列表中排名较低那么首先要转换为另外一个操作数的类型后执行运算。 但是算术转换要合理要不然会有一些潜在的问题。
float f 3.14;
int num f;//隐式转换会有精度丢失11.3操作符的属性
复杂表达式的求值有三个影响的因素。
操作符的优先级操作符的结合性是否控制求值顺序。 两个相邻的操作符先执行哪个取决于他们的优先级。如果两者的优先级相同取决于他们的结合性. 部分操作符优先级 一些问题表达式
//表达式的求值部分由操作符的优先级决定。
//表达式1
a*b c*d e*f注释代码1在计算的时候由于*比的优先级高只能保证的计算是比早但是优先级并不能决定第三个比第一个早执行。 所以表达式的计算机顺序就可能是
a*b
c*d
a*b c*d
e*f
a*b c*d e*f或者
a*b
c*d
e*f
a*b c*d
a*b c*d e*f//表达式2
c --c;注释同上操作符的优先级只能决定自减–的运算在的运算的前面但是我们并没有办法得 知操作符的左操作数的获取在右操作数之前还是之后求值所以结果是不可预测的是有歧义 的。 //代码3
#include stdio.h
int fun()
{static int count 1;return count;
}
int main()
{int answer;answer fun() - fun() * fun();printf(%d\n, answer);//输出多少return 0;
}这个代码虽然在大多数的编译器上求得结果都是相同的。 但是上述代码 answer fun() - fun() * fun(); 中我们只能通过操作符的优先级得知先算乘法再算减法。 函数的调用先后顺序无法通过操作符的优先级确定。
//代码4#include stdio.h
int main()
{int i 1;int ret (i) (i) (i);printf(%d\n, ret);printf(%d\n, i);return 0;
}
//尝试在linux 环境gcc编译器VS2013环境下都执行看结果vs2013运行结果 看看同样的代码产生了不同的结果这是为什么 简单看一下汇编代码.就可以分析清楚. 这段代码中的第一个 在执行的时候第三个是否执行这个是不确定的因为依靠操作符的优先级和结合性是无法决定第一个 和第三个前置 的先后顺序。 总结 我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径那这个表达式就是存在问题的。