文章目录
移位操作符
移位操作符 适用于 整形数据,只能 移动 正整数位,移动负整数是错误的写法。
<< 左移操作符
#include<stdio.h>
int main()
{
//对于整数的二进制有三种表达形式:原码,反码,补码
//正整数 原码 反码 反码都一样
int a = 5; //存放的的是补码,printf打印的是原码
// 00000000000000000000000000000101 原码
// 00000000000000000000000000000101 反码
// 00000000000000000000000000000101 补码
int c = -1;
// 10000000000000000000000000000001 //原码
// 11111111111111111111111111111110 //反码
// 11111111111111111111111111111111 //补码
原码 - 直接按照数字的正负写出二进制序列
反码 - 原码的符号位不变,其他位 按位取反
补码 - 反码 加 1
//整数
int b = a << 1;//左移操作符:左边丢弃,右边补零
printf("a = %d\n",a);// 5
移位前,
//00000000000000000000000000000101 //补码
//移位操作符:移动的是二进制位
移位后, a 不变,因为移位后,并没有将移位后的值,赋给 a,而是赋给了 b
//00000000000000000000000000001010//移位后
printf("b = %d\n",b);// 10
int d = c << 1;
// 11111111111111111111111111111111 -1 的 补码
// 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
//如果使用逻辑移位,移动2位的话, 想象一下,值和符号都会发生变化
printf("d = %d\n", d); //当前电脑 vs2013 运用的是算术右移
//打印的是原码 值为 -1
// 10000000000000000000000000000001 原码
return 0;
}
位操作符:
下面的为操纵符 都是 按二进制位(补码)进行逻辑计算
& 按位与
| 按位或
^ 按位异或
& 按位与 (二进制位(补码)进行逻辑计算)
双1出1,有0出0, 【C = A&B,假设 A,B 都是 1,C才能是 1,如果 A,B中有一个 为 0,即 C 等于 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 的补码
printf("c = %d\n",c);
//符号位为0;正整数 所以 三码相同
//00000000000000000000000000000010
%d 说明我们打印的是 有 符号数
%u 说明我们打印的是 无 符号数
所以最终结果为 2
return 0;
}
| 按位或 (二进制位(补码)进行逻辑计算)
有1出1,双0出0【假设 C = A | B, 只有 A,B 全部为 0,C才等于 0,只要 A,B中有一个为1,C 就等于 1】
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011 三码相同,整数在内存中存的是补码
int b = -2;
//10000000000000000000000000000010 原码
//11111111111111111111111111111101 反码 == 原码符号位不变,其他位按位取反
//11111111111111111111111111111110 补码 == 反码 + 1
int c = a | b;
//00000000000000000000000000000011 // a 补码
//11111111111111111111111111111110 // b 补码
//11111111111111111111111111111111 // c 的补码
printf("c = %d\n", c);// -1
打印是打印原码
11111111111111111111111111111111 补码
10000000000000000000000000000000 反码 == 补码 符号位不变,其他按位取反
10000000000000000000000000000001 原码 == 反码 + 1
最高位是符号位 为 1, 代表为负数,二进制最低位 为 1,转换成 十进制数 2^0 == 1
结合符号位 ,这个整数为 -1
return 0;
}
^ 按位异或 相异出1,相同出0
#include<stdio.h>
int main()
{
int a = 3;
int b = -2;
10000000000000000000000000000010 原码
11111111111111111111111111111101 补码
11111111111111111111111111111110 补码
int c = a ^ b;
//00000000000000000000000000000011 a 补码
//11111111111111111111111111111110 b 补码
//11111111111111111111111111111101 c 的补码
//10000000000000000000000000000010 c 的反码
//10000000000000000000000000000011 c 的原码
printf("c = %d\n", c);// -3
return 0;
}
不创建变量,交换两个变量 ( 0^b=b)&&(a异或 a 等于 0,相同为 0;b异或 b 等于 0,相同为 0)
#include<stdio.h>
int main()
{
int a = 0;
int b = 15;
a = a^b;
b = a^b;// a^b^b 》 b^b == 0[相同为 0],0^a == a
// b = a;
a = b^a;// a^a^b 同理 a^a = 0,0^b 就是 b
// a == b
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;赋给x,再赋给a
printf("%d", a);
return 0;
}
#include<stdio.h>
int main()
{
int a = 10;
00000000 00000000 00000000 00001010 三吗相同
a = a >> 1;// a == 5
00000000 00000000 00000000 00000101 (编译器一般都是 算术逻辑,移动一位后,最高位空出来了,根据原先的符号位。进行补位,这里原先的符号位为0,所以空出的位置 补 0)
a >>= 1; // a == 2
00000000 00000000 00000000 00000010
a = a + 10;
// a = 2 + 10 = 12
a += 10;// a = 12 + 10 = 22
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
//所以 b == 0.,打印 0
if (a != 0)//a不为假,打印hehe
{
printf("hehe\n");// a = 5,a为真,打印 hehe
}
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',就停止计算(计算的元素个数不包含 '\0')
// 且不能省略().
int arr[10] = { 0 };
// arr 数组名 通常 表达的意思 是 数组首元素地址
只有两种特殊情况
&arr 和 sizeof(arr) 中的 arr 都是代表整个数组【数组的地址】
// &arr[0]//数组首元素地址,sizeof(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类型,算变量或者类型创建变量的内存大小,和内存中存放什么没有关系
sizeof计算的类型的字节大小,并不涉及实际计算,short 为短整型,字节大小为 2,即输出结构为 2
另外 它计算玩 short 之后,后面 a + 1 ,它管都不管。所以 a+1 根本没机会运行
s 还是等于 10
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为正整数 三码一样,这是补码
a |= (1 << 1);//00000000000000000000000000001111 // a 与 1<< 1 逻辑或之后,把 1101中的0改成了 1 ,即1111
// 1的补码
//00000000000000000000000000000001
//1 << 1(1的补码向左移动一位)
//即:00000000000000000000000000000010
printf("%d\n", a);// 值为 15
// 00000000000000000000000000001111
a &= (~(1 << 1));
//00000000000000000000000000000010 1 << 1
按位取反
// 11111111111111111111111111111101
&
// 00000000000000000000000000001111
即
// 00000000000000000000000000001101
printf("%d\n", a); //13
return 0;
}
/前置加加 / 减减 ; 后置加加 / 减减
程序一:
#include<stdio.h>
int main()
{
int a = 10;
int b = a++;//后置加加,先赋值,后加加(把 a = 10的时候 ,赋给 b【b == 10】,然后 a 开始自增加一【a == 11】)
printf("%d\n",b);//10
printf("%d\n", a);//11
return 0;
}
程序二:
#include<stdio.h>
int main()
{
int a = 10;
int b = ++a;//前置加加,先加加,后赋值(a先自增加一【a == 11】,然后再将其 赋予 b【b == 11】)
printf("%d\n",b);//11
printf("%d\n", a);//11
return 0;
}
程序三:
#include<stdio.h>
int main()
{
int a = 10;
int b = a--;//后置减减,先赋值,后减减(先把 a == 10 的时候,赋给 b【b == 10】,然后 a 开始 自减减一【a == 9】)
printf("%d\n", b);//10
printf("%d\n", a);//9
return 0;
}
程序四:
#include<stdio.h>
int main()
{
int a = 10;
int b = --a;//前置减减,先减减,后赋值(先 a 自减减一【a == 9】,再将其值 赋给 b【b == 9】)
printf("%d\n", b);//9
printf("%d\n", a);//9
return 0;
}
() 强制转换符
#include<stdio.h>
int main()
{
int a = (int)3.14;// 把 3.14 强制类型转换,等于 3,【取整数部分】
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,如果 a,b中有一个为 0(任何一个为假),则 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。(a,b中只要有一个正整数【只要有一个真】,c 就为真,即 c == 1 )
return 0;
}
>= <=
#include<stdio.h>
int main()
{
int age = 0;
scanf("%d",&age);
if ((age >= 10) && (age <= 30))
{
;// 只有满足上述条件(age 必须大于等于10,小于等于30,两者缺一 不可),才执行 该语句
}
if ((age >= 10) || (age <= 30))
{
;// 只有满足上述条件(age 必须大于等于10 或 小于等于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;所以为假,与任何数相与都为假。 所以 i == 0,另外 ++b 和 d++ 没有执行,因为 && 与逻辑操作符,只关注真假,而且只有 有一个假,那么假,后面的数据它就不在管了,因为 && 符号 有一个为假,整体就为假。
用完之后 a自增加一, 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,为真,三个条件都为真,所以 i 为真(&&的规则),故 i == 1
用完之后 a=2(a在调用完后,自增加一【a == 2】),b= 3,c=3,d=5(d在调用完后,自增加一【b == 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(a在被调用完之后,自增加一【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(a在被调用完之后,自增加一【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) 的意思是 如果 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, b = a + 1/* 13 */);
a > b :只是执行一下(只是判断一下)
a = b + 10 :此时 a == 12
a :意思就是 a == 12
b = a + 1 :此时 b == 13
逗号表达式 从左往右计算,以 最右边的式子 的 结果 为 整个式子的结果(b == 13 , 即 c == 13)
printf("%d\n",c);// 13
return 0;
}
程序二:
/#include<stdio.h>
int main()
{
int a = get_val();
count_val(a);
while (a > 0)//while (int 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 元素的地址,(第 i - 1 个元素的地址)
printf("%p\n",arr+i);//与上式子完全等价,数组名 arr,没有和 & 和 sizeof 联合使用,所以表达的是 首元素地址,想得到后面的元素的地址很简单,加上 i(注意 i 的类型与 数组 元素类型相同,+ i,等于跳过 i 个元素【举个例子:arr + 1,arr代表首元素地址,对其加一,等于 跳过 一个 int 类型的内存大小,而数组arr的元素的类型就 int,就是说跳过 首元素,指向下一个元素的起始地址】)
}
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;
}
结构访问操作符 . 和 ->
例:
结构体变量.成员名
结构体指针 -> 成员名 -> 指针型
#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);
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的整形算术运算 总是 至少 以缺省整形类型的精度来进行的(缺省 的意思是 系统默认状态,就是说系统总是 以 int 类型 的 精度 来进行运算的)
为了获得这个精度,表达式中 的 字符 和 短整型操作数 在使用之前 被转换为普通整形,这种转换 称为 整形提升。
参考程序:
#include<stdio.h>
int main()
{
char a = 3;//char 1 byte 只能存放 8 bit
000000000000000000000000 00000011 // 3 的 补码 。 a 是正整数 三码相同
00000011 //数据截断(最低位)后的数据,这就是存入a的数据
char b = 127;
//000000000000000000000000 01111111 // 127的二进制位
//01111111 这是真正存入b的二进制位数据
char c = a + b;
// b 和 a 想要进行运算,就必须提升为普通整形,再进行加法运算,
//加法运算完以后,结果将被截断(将整形数据存入字符类型数据中),然后存储于c中
// a 和 b 如何相加 表达式中的 字符 和 短整型操作数 在使用之前 被转换为普通整形,这种转换称为 整形提升。
//00000011 整形提升 是 按照 变量的数据类型 的 符号位 来提升的。
//01111111 因为截断后的数据第一位是0,即为正,根据变量类型的符号位,来进行整形提升
// 补0,直至 32 bit(4 byte)
注意 无符号整形 整形提升, 直接补0
整形提升后
//000000000000000000000000 00000011 a
//000000000000000000000000 01111111 b
//000000000000000000000000 10000010 // 加法运算完后,a+b
再进行截断 10000010(补码) 这就是放进c的二进制位
printf("%d\n",c);// 打印原码 :100000000000000000000000 01111110 -126
因为要 打印,所以又要进行整形提升
111111111111111111111111 10000010(补码) //整形提升:按照变量的数据类型的符号位(10000010)来提升的,所以补1,
100000000000000000000000 01111101 反码
100000000000000000000000 01111110 原码
return 0;
}
整形提升的意义:
表达式 的 整形运算 要在 CPU 的 相应运算器件内执行,CPU内整形运算器(ALU) 的 操作数 的 字节长度 一般 就是 int 的字节长度,同时也是 CPU 通用 的 寄存器的长度。
因此,即使 两个char类型 的 相加,在 CPU执行时 实际上 也要 先转换为 CPU 内 整形操作数 的 标准长度。
通过CPU(general-purpose CPU)是 难以 直接实现 两个8字节 直接相加运算(虽然 机器指令中 可能有 这种字节 相加的指令)。
所以, 表达式中 各种长度 可能小于 int 长度 的 整型值,都必须先转换为 int 或 unsigned int ,然后才能送入CPU执行运算
案例 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;// *(乘) 的优先级比 + 的优先级要高,所以要先计算 b*3,在计算 a + z(假设 z 是 b*3得出的值)
printf("%d\n",c);// 20*3+10= 70
// 2, 结合性
int d = a + b + 3; // + 的结合性 LR 从左往右计算
10 + 20 + 3 == 30 + 3 == 33
printf("%d\n", d);// 33
// 3. 是否控制求值顺序
int e = a && b + 3;// 因为 + 比 && 的优先级要高,所以先计算 b+3,再根据 逻辑与 && 结合性 LR,如果 a 为假的话,后面的式子得出的值就不重要了,甚至可能 b+3 都不会去计算,直接为假,e == 0,这就是控制求值顺序
// 逻辑或 || 条件操作符(三目操作符)()?():() 逗号表达式【结果为最后一个表达式的结果】 , 也是一样的。
printf("%d\n", e); // 0
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(1)= 1 + 0 == 1
左操作数的 获取,在有 操作数 之后,假设 c = 1 表达式为 c( )+ --c(1)= 0 + 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 按照这个顺序 LR(从左往右的方式) 调用 2 - 3 *4 = -10
// 2 3 1 按照这个顺序 即 3 - 4 * 2 = -5
// 3 2 1 按照这个顺序 即 4 - 3 * 2 = -2
printf("%d\n",answer);
return 0;
}