#define _CRT_SECURE_NO_WARNINGS 1
//移位操作符:适用于整形数据
//只能移动正整数位,移动负整数是错误的写法。
// << 左移操作符
//#include<stdio.h>
//int main()
//{
//
//
// //对于整数的二进制有三种表达形式:原码,反码,补码
//
// //正整数 原码 反码 反码都一样
// int a = 5; //存放的的是补码,printf打印的是原码
// // 00000000000000000000000000000101 //原码
// // 00000000000000000000000000000101 //反码
// //
// int c = -1;
10000000000000000000000000000001 //原码
11111111111111111111111111111110 //反码
11111111111111111111111111111111 //补码
// //负整数 - 原码 - 直接按照数字的正负写出二进制序列
// //反码 - 原码的符号位不变,其他位 按位取反
// //补码 - 反码 加 1
//
// //整数
// int b = a << 1;//左移操作符:左边丢弃,右边补零
// printf("a = %d\n",a);// 5
// //移位后,a 不变
// //00000000000000000000000000000101 //补码
// //移位操作符:移动的是二进制位
// //00000000000000000000000000001010//移位后
// printf("b = %d\n",b);// 10
//
// //负数
// int d = c << 1;
11111111111111111111111111111111 //补码
11111111111111111111111111111110 //移位后
// printf("d = %d\n", d);//打印的是原码 -2
11111111111111111111111111111110//补码
11111111111111111111111111111101//反码 = 补码 - 1
10000000000000000000000000000010//原码 = 反码符号位不变,其余按位取反
// return 0;
//}
// >>右移操作符 :右移有2种方式
//1.逻辑移位 右边丢弃,左边补零。
2.算术移位 右边丢弃,左边用原该值的符号位填充。(最常用)
//#include<stdio.h>
//int main()
//{
// int a = 5;//因为 a = 5 是整数,即 原反补 三码相同
// int b = a >> 1;
// // 00000000000000000000000000000101 //原码
// // 00000000000000000000000000000010 //移位后的原码()
// printf("b = %d\n",b);// 2
//
// int c = -1;//根据 -1 的值写出原码 - 反码 - 补码
// //10000000000000000000000000000001 //原码
// //11111111111111111111111111111110 //反码
// //11111111111111111111111111111111 //补码
// int d = c >> 1;
// //11111111111111111111111111111111 //算术移位:右边丢弃,左边用原该值的符号位填充。
// //值不变 -1
//
// //01111111111111111111111111111111 //逻辑移位: 右边丢弃,左边补零。
原码 00000000000000000000000000000001 值为 1
// //值的符号都变了
//
// printf("d = %d\n", d); //当前电脑 vs2013 运用的是算术右移
// //打印的是原码 值为 -1
// //10000000000000000000000000000001
// return 0;
//}
//位操作符:
// 都是按二进制位(补码)进行逻辑计算
// & 按位与
// | 按位或
// ^ 按位异或
// & 按位与 双1出1,有0出0
//#include<stdio.h>
//int main()
//{
// int a = 3;// 3 为正整数
// //00000000000000000000000000000011 //原码 = 反码 = 补码
// int b = -2;
// //10000000000000000000000000000010 //原码
// //11111111111111111111111111111101 //反码
// //11111111111111111111111111111110 //补码
// int c = a & b;
// //00000000000000000000000000000011 // a 补码
// //11111111111111111111111111111110 // b 补码
// //00000000000000000000000000000010 // c 的补码
// //符号位为0;正整数 所以 三码相同
// printf("c = %d\n",c);
// //%d 说明我们打印的是 有 符号数
// //%u 说明我们打印的是 无 符号数
// return 0;
//}
// | 按位或 有1出1,双0出0
//#include<stdio.h>
//int main()
//{
// int a = 3;
// //00000000000000000000000000000011 //补码
// int b = -2;
// //11111111111111111111111111111110 //补码
// int c = a | b;
// //00000000000000000000000000000011 // a 补码
// //11111111111111111111111111111110 // b 补码
// //11111111111111111111111111111111 // c 的补码
// printf("c = %d\n", c);// -1
// return 0;
//}
// ^ 按位异或 相异出1,相同出0
//#include<stdio.h>
//int main()
//{
// int a = 3;
// int b = -2;
// int c = a ^ b;
// //00000000000000000000000000000011 // a 补码
// //11111111111111111111111111111110 // b 补码
// //11111111111111111111111111111101 // c 的补码
//
// //10000000000000000000000000000011 // c 的原码
// printf("c = %d\n", c);// -3
// return 0;
//}
//
//不创建变量,交换两个变量 (a^a=0,0^b=b)
//#include<stdio.h>
//int main()
//{
// int a = 0;
// int b = 15;
// a = a^b;
// b = a^b;
// a = b^a;
// printf("%d\n", a);
// printf("%d\n", b);
// return 0;
//}
//连续赋值
//#include<stdio.h>
//int main()
//{
// int a = 10;
// int b = 10;
// int x = 0;
// a = x = b + 1;//(建议)x=b+1; a=x;
先计算b+1;再赋给a,再赋给b
// printf("%d", a);
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int a = 10;
// //1.
// a = a >> 1;
// //2.
// a >>= 1;
//
// //1.
// a = a + 10;
// //2.
// a += 10;
// return 0;
//}
//单目操作符(unary operator)
//#include<stdio.h>
//int main()
//{
// // ! 逻辑反操作
// int a = 5;
// int b = !a;
// printf("%d\n", b);//a=非零为真,!a==0 把真变为假,反之 a = 0 !a== 1
// if (a != 0)//a为真,打印hehe
// {
// printf("hehe\n");
// }
// if (!a)//a为假,打印haha
// {
// printf("haha\n");
// }
// return 0;
//}
// &取地址 *解引用符 sizeof:
//#include<stdio.h>
//#include<string.h>
//int main()
//{
//
// char array[10] = "abc";// 10个字节的内存
// printf("%d\n",sizeof(array));// 10
// printf("%d\n", sizeof array);//两者等价,因为sizeof操作符不是函数,所以可去括号
// //如果括号里是类型:sizeof(int),此处括号不可省略
// //计算变量或者类型创建变量的内存大小,单位字节,和内存中存放什么没有关系
// printf("%d\n", strlen(array)); // 3
// // strlen(arr) 遇到'\0',就定制计算
// // 且不能省略().
//
// int arr[10] = { 0 };
// // arr //数组首元素地址
// // &arr //取数组地址 == sizeof(arr) 中的 arr 都是代表整个数组
// // &arr[0]//数组首元素地址
// // &arr[9]//数组第10个元素地址
// int a = 10;
// int* p = &a;
// *p = 20;//*p仿作左边代表的是用来存放数值的空间
// printf("%d\n", a);// 20
// int b = *p;// *p放在右边代表的是存储的空间里存储的数值
// printf("%d\n",b);// 20
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int a = 5;
// short s = 10;
// printf("%d\n", sizeof(s = a + 1));// 2 s是短整型,a+1=6,把这个整形放进短整型,放不下,发生截断
// //最终的结果,归s负责,为short类型,算变量或者类型创建变量的内存大小,和内存中存放什么没有关系
// //即为 2
//
// printf("%d\n",s);// 10 sizeof不参与计算,直接计算出sizeof(short)拿走了,所以后面 a+1;根本就没机会运行,所以s还是10.
// return 0;
//}
// ~ 按位取反(包括符号)
//#include<stdio.h>
//int main()
//{
// int a = 0;
// //00000000000000000000000000000000
// int b = ~a;
// //11111111111111111111111111111111 //补码
// printf("%d",b);// 原码= (补码 - 1)取反 (不包括符号位)
// //10000000000000000000000000000001
// // 值为 -1
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int a = 13;
// //00000000000000000000000000001101 // a为正整数 三码一样,这是补码
//
// // 1的补码
// //00000000000000000000000000000001
//
// //1 << 1(1的补码向左移动一位)
// //即:00000000000000000000000000000010
// a |= (1 << 1);//00000000000000000000000000001111//经由上两式子逻辑或之后,把 1101中的0改成了 1 ,即1111
// printf("%d\n", a);// 值为 15
// // 00000000000000000000000000001111
//
// //00000000000000000000000000000010 1 << 1
// //按位取反
// // 11111111111111111111111111111101 & 00000000000000000000000000001111
// a &= (~(1 << 1));//00000000000000000000000000001101
// printf("%d\n", a); //13
//
//
// return 0;
//}
//前置加加/减减 后置加加/减减
//#include<stdio.h>
//int main()
//{
// int a = 10;
// int b = a++;//后置加加,先赋值,后加加
// printf("%d\n",b);//10
// printf("%d\n", a);//11
// return 0;
//}
//
//#include<stdio.h>
//int main()
//{
// int a = 10;
// int b = ++a;//前置加加,先加加,后赋值
// printf("%d\n",b);//11
// printf("%d\n", a);//11
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int a = 10;
// int b = a--;//后置减减,先赋值,后减减
// printf("%d\n", b);//10
// printf("%d\n", a);//9
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int a = 10;
// int b = --a;//前置减减,先减减,后赋值
// printf("%d\n", b);//9
// printf("%d\n", a);//9
// return 0;
//}
// ()强制转换符
//#include<stdio.h>
//int main()
//{
// int a = (int)3.14;
// printf("%d\n",a);// 3
// return 0;
//}
// && 逻辑与 只关注真假
//#include<stdio.h>
//int main()
//{
// int a = 1;
// int b = 2;
// int c=a&&b;
// printf("%d\n",c);// a 和 b 都是非零,所以c为真 c == 1
// return 0;
//}
// && 逻辑或,有1出1,双零出零
//#include<stdio.h>
//int main()
//{
// int a = 1;
// int b = 0;
// int c = a||b;
// printf("%d\n", c);// a 是非零,所以c为真 c == 1
// return 0;
//}
// >= <=
//#include<stdio.h>
//int main()
//{
// int age = 0;
// scanf("%d",&age);
// if ((age >= 10) && (age <= 30))
// {
// ;
// }
// if ((age >= 10) || (age <= 30))
// {
// ;
// }
// return 0;
//}
// 360 笔试题
//#include<stdio.h>
//int main()
//{
// int i = 0, a = 0, b = 2, c = 3, d = 4;
// i = a++ && ++b && d++; // a++ 先赋值再加加,a=0;所以为假,与任何数相与都为假。 所以 && ++b && d++ 没有执行
// //用完之后 a加加等于 1
// printf("a = %d b = %d c = %d d = %d\n",a,b,c,d);//即输出位 1,2,3,4
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int i = 0, a = 1, b = 2, c = 3, d = 4;
// i = a++ && ++b && d++; // a=1;所以为真, ++b先加后赋值,即b==3, d++先加后赋值,此时d=4
// //用完之后 a=2,b= 3,c=3,d=5
// printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);//即输出位 2335
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int i = 0, a = 1, b = 2, c = 3, d = 4;
// i = a++ || ++b || d++; // a=1;所以为真, 或逻辑 只要一个为真,就是真,所以后面的|| ++b || d++ 都不执行
// //用完之后 a=2,b= 3,c=3,d=4
// printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);//即输出位 2234
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int i = 0, a = 0, b = 2, c = 3, d = 4;
// i = a++ || ++b || d++; // a=0;所以为假,++b,先加后赋,b==3, 因为或逻辑只要一个为真,就是真,所以后面的 || d++ 都不执行
// //用完之后 a=1,b= 3,c=3,d=4
// printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);//即输出位 1334
// return 0;
//}
//条件操作符 ()?():() 又称 三目操作符
//#include<stdio.h>
//int main()
//{
// int a = 10;
// int b = 20;
// int c = (a > b) ? (a) : (b);// 如果a>b为真, 就返回a,否则返回 b
// printf("%d\n",c);//a > b 为假,所以返回 b
// return 0;
//}
//逗号表达式
//#include<stdio.h>
//int main()
//{
// int a = 1;
// int b = 2;
// int c = (a > b/*只是执行一下*/, a = b + 10/* a == 12 */, a/* 12 */, b = a + 1/* 13 */);//从左往右计算,以为 最右边的式子 的 结果 为 整个式子的结果
// printf("%d\n",c);// 13
// return 0;
//}
//#include<stdio.h>
//int main()
//{
// int a = get_val();
// count_val(a);
// while (a > 0)//while (a = get_val(),count_val(a);a > 0) //逗号表达式可以运用到循环中,从左往右计算
// {
// ;
// }
//
// return 0;
//}
//下标引用操作符[],函数调用操作符(),结构访问操作符
//下标引用操作符
//#include<stdio.h>
//int main()
//{
// int arr[] = { 1, 2, 3, 4, 5 };
// int i = 0;
// //arr[4]// 代表的是数组中第五个元素 5,等价于 *(arr+4)== *(4+arr) 和 4[arr].,说明这些式子支持交换律
// // []是一个操作符,而 4 和 arr 是操作数
//
// // *(arr+4) == *(4+arr) 意思是用 arr 的 起始地址 加上 4 ,找到下标为4 的元素
// //该式子支持交换律
// for (i = 0; i < 5; i++)
// {
// printf("%p\n",&arr[i],arr+i);//输出的数列arr中第 i+1 个元素的地址
// //printf("%p\n",arr+i);//与上式子完全等价
// }
// return 0;
//}
//函数调用操作符
// 能接受一个或者多个操作数;第一个操作数是函数名,其余操作数就是专递函数的参数
//
//#include<stdio.h>
//#include<string.h>
//
//void test()
//{
// printf("hehe\n");
//}
//
//int main()
//{
// char arr[] = "hello world1";
// strlen(arr);// 这里的小圆括号,是函数调用操作符,不可省略(strlen arr // error)
// //strlen 函数的返回值是 size_t == unsigned int 无符号整形
//
// printf("%u\n",strlen(arr));// %u,表示打印无符号整形,%d 表示打印有符号整形
// printf("%u\n", strlen("hello"));//函数调用操作符() ,不可省略
//
//
// test();//函数调用操作符() ,不可省略.即使不传参,也是一样。
//
// return 0;
//}
//结构访问操作符 . 例:结构体.c成员名 -> 指针型 : 结构体指针 -> 成员名
//#include<stdio.h>
自定义类型
//struct book
//{
// char name[20];
// float price;
// char id[20];
//};
//void print(struct book* pb)
//{
// //常用后者表示
// printf("%s\n", (*pb).name);//printf("%s\n", pb->name);
// printf("%f\n", (*pb).price);//printf("%f\n", pb->price);
// printf("%s\n", (*pb).id);//printf("%s\n", pb->id);
//}
//int main()
//{
// struct book b = { "z语言",48.5f, "ab12345" };
//
// printf("%s\n",b.name);//printf("%s\n",b.name);
//
// strcpy(b.name,"c");//改变 或 赋值 数据结构,需要通过 strcpy 函数
// //因为数组名是地址,数据结构需要放在储存空间里
// b.price = 50; // 但变量可以直接赋值
//
// print(&b);
//
// printf("%s\n", b.name);
// printf("%f\n", b.price);
// printf("%s\n", b.id);
// return 0;
//}
//表达式求值的顺序
// 一部分有操作符的优先级和结合性决定。
//同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
//隐式类型转换 :偷偷的进行类型转换
//c的整形算术运算总是至少以缺省整形类型的精度来进行的
//为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整形,这种转换称为 整形提升。
//#include<stdio.h>
//int main()
//{
// char a = 3;//char 1 byte 只能存放 8 bit
// //000000000000000000000000 00000011 //补码 。 a 是正整数 三码相同
// //00000011 //数据截断(最低位)后的数据,也就是存入a的数据
// char b = 127;
// //000000000000000000000000 01111111 // 127的二进制位
// //01111111 这是真正存入b的二进制位
//
//
// // b 和 a 的值都被提升为普通整形,再进行加法运算,
// //加法运算完以后,结果将被截断(整形存入字符类型),然后存储于c中
// char c = a + b;
// // a 和 b 如何相加 表达式中的字符和短整型操作数在使用之前被转换为普通整形,这种转换称为 整形提升。
// //00000011 整形提升是按照变量的数据类型的符号位来提升的。
// //01111111 因为截断后的数据第一位是0,即为正,根据变量类型的符号位,来进行整形提升
// 无符号整形直接补0
//
// //整形提升后
// //000000000000000000000000 00000011
// //000000000000000000000000 01111111
// //000000000000000000000000 10000010 // 加法运算完后,a+b
// //再进行截断 10000010(补码) 这就是放进c的二进制位
// //11111111111111111111111110000010(补码) //整形提升:按照变量的数据类型的符号位(10000010)来提升的,所以补1,
// printf("%d\n",c);// 打印原码 :100000000000000000000000 01111110 -126
//
// //整形提升的意义
// //表达式的整形运算要在CPU的相应运算器件内执行,CPU内整形运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU通用的寄存器的长度。
//
// // 因此,即是两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整形操作数的标准长度。
//
// //通过CPU(general-purpose CPU)是难以直接实现两个8字节直接相加运算(虽然机器指令中可能有这种字节相加的指令)。
// //所以,表达式中各种长度可能小于 int 长度的整型值,都必须先转换为 int 或 unsigned int ,然后才能送入CPU执行运算
// return 0;
//}
//案例 1
//#include<stdio.h>
//int main()
//{
// char a = 0xb6;// 0xb6 二进制码 1011 0110 存入 a ;然后 0xb6 == a要进行比较,所以 a 要整形提升,按照变量的数据类型的符号位,即 1。所以补 1
// //111111111111111111111111 1011 0110 //整形提升后,很明显 a 与 0xb6 不相等,所以不打印 a
// short b = 0xb6000; // 与上式子同理,所以 b 也不打印
// int c = 0xb6000000;//因为 c 本来就是整形,所以不用整形提升,即if (0xb6000000 == c)条件成立,所以可以打印 c
// if (0xb6 == a)
// {
// printf("a");
// }
// if (0xb6000 == b)
// {
// printf("b");
// }
// if (0xb6000000 == c)
// {
// printf("c");
// }
// return 0;
//}
//案例 2
//#include<stdio.h>
//int main()
//{
// char c = 1;//char 1 byte
// printf("%u\n",sizeof(c)); // 1
// printf("%u\n", sizeof(+c));// 4 要进行整形计算了,所整形提升,故32 bit,即4字节。也就说sizeof计算的是一个整形的大小
// printf("%u\n", sizeof(!c));// 1
// return 0;
//}
//算术转换
//如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换 为另一个操作数的类型,否则操作就无法进行。
//寻常算术转换
// long double
// double
// float
// unsigned long int
// long int
// unsigned int
// int
//如果程序中出现上面其中2个类型,下面的要向上面的进行 算术转换
// 比如 出现 int 和 unsigned int, int 要转换成 unsigned int 进行运算——算术转换
//操作符的属性
//复杂表达式的求值有三个影响的因素
// 1. 操作符的优先级
// 2. 操作符的结合性
// 3. 是否控制求值顺序
// 两个相邻的操作符执行哪一个?取决于它们的优先级,如果两者的优先级相同,取决于它们的结合性。
//#include<stdio.h>
//int main()
//{
// int a = 10;
// int b = 20;
// // 1. 优先级
// int c = a + b*3;// *(乘) 的优先级比 + 的优先级要高,所以要先计算 a*3,在计算 + a
// printf("%d\n",c);// 20*3+10= 70
// // 2, 结合性
// int d = a + b + 3; // + 的结合性 LR 从左往右计算
// printf("%d\n", d);
// // 3. 是否控制求值顺序
// int e = a && b + 3;// 逻辑与 && 结合性 LR,如果 a为假的话,后面的式子,他就不会计算了,这就是控制求值顺序
// // 逻辑或 || 条件操作符(三目操作符)()?():() 逗号表达式 , 也是一样的。
// //结果为最后一个表达式的结果
// printf("%d\n", e);
// return 0;
//}
//一些问题表达式 (重点: 最好能确定唯一的计算路径,如果不能,说明这个表达式就是存在问题的)
// 代码1. a * b + c * d + e * f 绝对不能写出这种的表达式,如果非要写,写好()因为它的优先级最高,
// 比如 ,我想先计算a * b ,后c * d,再e * f
//即((a * b )+ c * d )+ e * f
//代码在计算的时候由于*的优先级高,只能保证比+早,但是优先级并不能决定 三个*中哪一个早执行。
// 代码2. c + --c; // --优先级高,先--c,再加 c
// 但是 + 操作符的 左操作数的 获取,在 右操作数 之前 还是 之后 求值,我们不能确定,所以结果不可预测,是有歧义的
//左操作数的 获取,在 右操作数 之前 比如说 c = 1 表达式为 c(1)+--c(0)=1
//左操作数的 获取,在有 操作数 之后, c = 1 表达式为 c(0)+ --c(0)=0
// 代码3. 非法表达式
//#include<stdio.h>
//int main()
//{
// int i = 10;
// i = i-- - --i * (i = -3) * i++ + ++i;//error 错误写法,最好能确定唯一的计算路径。
// printf("i = %d\n", i);//在不同的编译器里,输出也不同
// //在vs2013 中输出为 4
// return 0;
//}
// 代码 4.
#include<stdio.h>
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();// 错误在于,不确定这三个 fun()函数,谁先调用
// 1 2 3 按照这个顺序调用 2 - 3 *4 = -10
// 2 3 1 3 - 4 * 2 = -5
// 3 2 1 4 - 3 * 2 = -2
printf("%d\n",answer);
return 0;
}