C语言进阶学习5 逻辑运算符,位运算符,++和--操作,三目运算符和逗号表达式
本博客记录狄泰学院课程的学习
学习交流群:320628659
个人QQ:1367087622
欢迎大家交流学习
本章的学习目标:
逻辑运算符,位运算符,++和–操作,三目运算符和逗号表达式的学习分析
学习这些知识点使用需要注意的事项
逻辑运算符,位运算符,++和–操作,三目运算符和逗号表达式
逻辑运算符
逻辑运算符在条件判断时虽然看起来挺简单,但是因为使用的频率很高所以有很多需要注意的点需要记录学习
- 逻辑运算符==&&,||,!==真的很简单吗?
下面的程序运行结束后i,j,k的值分别为多少?
#include <stdio.h> int main() { int i = 0; int j = 0; int k = 0; ++i || ++j && ++k; printf("%d\n", i);//i=1; printf("%d\n", j);//j=0 printf("%d\n", k);//k=0 return 0; }
-
程序中的短路
||从左向右开始计算:
当遇到为真的条件时停止计算,整个表达式为真 - 所有条件为假时表达式才为假
&&从左向右开始计算:
-
当遇到为假的条件时停止计算,整个表达式为假
分析后得知,在i为真后就停止了计算。发生了短路。
-
在逻辑表达式中,==&&比||==具有更高的优先级
在==&&和||==混合运算时
整个表达式被看作==||==表达式,
-
编译器从左向右开始计算==&&表达式,当某个&&表达式的值为真时,停止计算,整个表达式的值为真==。
-
最后计算==||==表达式
++ i || ++j && ++k; ⇒ (++i)||(++j && ++k); ⇒ (true && ++i) || (++j && ++k);
程序中的短路规则
#include <stdio.h> int g = 0; int f() { printf("In f()...\n"); return g++; } int main() { if( g || f() && f() ) { printf("In if statement: %d\n", g); } printf("In main(): %d\n", g); return 0; }
分析:第14行化为(true && g)||(f() && f())
判断g时因为g为0为假,继续往右判断,到f()时打印了In f()…,到返回时因为是后置++所以先返回g的值为0在增1,g=1,那么右边的f()&&f()的逻辑表达式为假,就发生了短路后面的f()就不会计算了。
所以if的结果为假不执行里面的语句,最后输出In main():1,
逻辑非“!”究竟是什么?
- 在C语言中的逻辑非“!” ! 只认的0,只知道见了0就返回1。因此当其碰见的值不是0时,其结果为0
#include <stdio.h> int main() { printf("%d\n", !0); //1 printf("%d\n", !1); //0 printf("%d\n", !100); //0 printf("%d\n", !-1000); //0 return 0; }
位运算符
-
C语言中的位运算符
位运算符直接对bit位进行操作,其效率最高
& 按位与 | 按位或 ^ 按位异或 ~ 取反 << 左移 >> 右移
左移和右移注意点
左移操作数必须为整数类型
char和short被隐式转换为int后进行移位操作右移操作的范围必须为:[0, 31]
左移运算符<<将运算数的二进制位左移
-
规则:高位丢弃,低位补0
右移运算符>>把运算数的二进制位右移
-
规则:高位补符号位,低位丢弃(unsigned补0,signed补1)
#include <stdio.h> int main() { printf("%d\n", 3 << 2); //11 << 2 = 1100 = 12 printf("%d\n", 3 >> 1); //11 >> 1 = 20000 01 = 1 printf("%d\n", -1 >> 1); //-1 = 0000 0001各位取反后加1 = 1111 1111 //1111 1111 >> 1 = 0111 1111 补上符号位1111 1111结果没有变化还是-1 printf("%d\n", 0x01 << 2 + 3);//加法的运算符优先于<<,所以0x01 << 5 = 32 printf("%d\n", 3 << -1); // oops! //标准上只定义了右移操作的范围必须为:[0, 31], //超过这个范围,每个编译器的处理并不一样 return 0; }
-
小贴士
防错准则:
避免位运算符,逻辑运算符和数学运算符同时出现在一个表达式中 - 当避免不了时,混合计算时尽量使用括号来表达计算次序
嵌入式编程 有一个小技巧
-
左移n位相当于乘以2的n次方,但效率比数学运算符高
编程实验:交换两个整型变量的值
#include <stdio.h> #define SWAP1(a, b) \ { \ int t = a; \ a = b; \ b = t; \ } #define SWAP2(a, b) \ { \ a = a + b; \ b = a - b; \ a = a - b; \ } //最为高效 #define SWAP3(a, b) \ { \ a = a ^ b; \ b = a ^ b; \ a = a ^ b; \ } int main() { int a = 1; int b = 2; printf("a = %d\n", a); printf("b = %d\n", b); SWAP3(a ,b); printf("a = %d\n", a); printf("b = %d\n", b); return 0; }
- 位运算与逻辑运算
位运算与逻辑运算的不同:
-
位运算没有短路规则,每个操作数都参与运算
- 位运算的结果为整数,而不是0或1
- 位运算优先级高于逻辑运算优先级
运算优先级:四则运算 > 位运算 > 逻辑运算
++ 和 – 操作符
-
++和–操作符对应两条汇编指令
前置
变量自增(减)1 - 取变量值
后置
-
取变量值
一对令人头疼的兄弟
#include <stdio.h> int main() { int i = 0; int r = 0; r = (i++) + (i++) + (i++); printf("i = %d\n", i);//在gcc中 i = 3 printf("r = %d\n", r);//r = 0 r = (++i) + (++i) + (++i); printf("i = %d\n", i);// i = 6 printf("r = %d\n", r);// r = 16 return 0; }
分析一下我们的想法
(i++) + (i++) + (i++) ; //i -> 3
0 + 1 + 2 =3
(++i) + (++i) + (++i) ; //i -> 6
4 + 5 +6 =15
但是。。。在vs下是这样的结果
在gcc下
eclipse下
- C语言中只规定了++和–对应指令的相对执行次序
- ++ 和 – 对应的汇编指令不一定连续运行
- 在混合运算中,++和–的汇编指令可能被打断执行
- 不同的编译器在混合运算时处理并不一样
所以结论是++和–参与混合运算结果是不确定的
贪心法
- 贪心法:++,–表达式的阅读技巧 编译器处理的每个符号应该尽可能多的包含字符
- 编译器以从左向右的顺序一个一个尽可能多的读入字符
- 当读入的字符不可能和已读入的字符组成合法符号为止
- 编译器通过贪心法处理表达式中的子表达式
#include <stdio.h> int main() { int i = 0; int j = ++i+++i+++i;//贪心法,++i++ -> 1++会报错 int a = 1; int b = 4; int c = a+++b;//a++ +b int* p = &a; b = b/*p;// /* printf("i = %d\n", i); printf("j = %d\n", j); printf("a = %d\n", a); printf("b = %d\n", b); printf("c = %d\n", c); return 0; }
空格可以作为C语言中一个完整符号的休止符
编译器读入空格后立即对之前读入的符号进行处理
三目运算符
- 三目运算符(a ? b : c)可以作为逻辑运算符的载体
- 规则:当a的值为真时,返回b的值;否则返回c的值
- C语言中的三目运算符返回的是值,不是变量本身。
#include <stdio.h> int main() { int a = 1; int b = 2; int c = 0; c = a < b ? a : b; (a < b ? a : b) = 3;//在C语言中,三目运算符不能做左值, //在C++中会有升级,如果有逻辑部分有常量才不能做左值 *(a < b ? &a : b) = 3;//可以改变 printf("%d\n", a); printf("%d\n", b); printf("%d\n", c); return 0; }
- 三目运算符(a ? b : c)的返回类型 通过隐式类型转换规则返回b和c中的较高的类型
- 当b和c不能隐式转换到同一类型时将编译出错
#include <stdio.h> int main() { char c = 0; short s = 0; int i = 0; double d = 0; char* p = "str"; printf( "%d\n", sizeof(c ? c : s) );//c为真?c=0为假返回s的值 //char和short混合运算会转换为int的类型 printf( "%d\n", sizeof(i ? i : d) );//i为假返回d的值,返回值类型是double printf( "%d\n", sizeof(d ? d : p) );//无法隐式的将double转为char*,报错 return 0; }
逗号表达式
-
逗号表示式是C语言中”粘贴剂“
-
逗号表达式用于将多个子表达式连接为一个表达式
-
逗号表达式的值为最后一个子表达式的值
-
逗号表达式中的前N-1个子表达式可以没有返回值
-
逗号表达式按照从左向右的顺序计算每个子表达式的值
exp1,exp2,exp3,…,expN
逗号表达式的示例:
#include <stdio.h> void hello() { printf("Hello!\n"); } int main() { int a[3][3] = { (0, 1, 2),//这的逗号表达式的值为2 (3, 4, 5),//这的逗号表达式的值为5 (6, 7, 8)//这的逗号表达式的值为8 }; int i = 0; int j = 0; while( i < 5 ) printf("i = %d\n", i), hello(), i++; for(i=0; i<3; i++) { for(j=0; j<3; j++) { printf("a[%d][%d] = %d\n", i, j, a[i][j]); //打印的是2,5,8 } } return 0; }
一行代码实现strlen
#include <stdio.h> #include <assert.h> int strlen(const char* s) { return assert(s), (*s ? strlen(s + 1) + 1 : 0); } int main() { printf("len = %d\n", strlen("Delphi")); printf("len = %d\n", strlen(NULL)); return 0; }
- 三目运算符返回变量的值,而不是变量本身
- 三目运算符通过隐式类型转换规则确认返回值类型
- 逗号表达式按照从左向右的顺序计算每个子表达式的值
- 逗号表达式的值为最后一个子表达式的值
- c语言学习之基础知识点介绍(四):算术运算符和逗号表达式
- C语言学习笔记:06_逻辑运算符和逻辑表达式
- c语言 转义符 算术运算符及赋值运算符 复合运算符 逗号运算式和表达式 关系运算符 逻辑运算符
- Linux 数据库学习的准备工作---C语言操作数据库
- C语言学习之路之四-----------C语言的运算符与表达式
- 【原创】C语言逗号表达式
- C语言进阶之文件复制操作
- 就C语言的指针、链表的原理和各类操作撰写一篇技术博客,说说自己学习C语言指针和链表的体会,并将学生信息管理系统进行修改,使能完成其他的功能,并撰写体会,附加源代码。
- C语言的各种位运算符的操作简述
- C语言中的逗号表达式
- C语言逗号运算符和逗号表达式
- c语言中逗号运算符和逗号表达式
- c语言中逗号运算符和逗号表达式
- c语言学习之基础知识点介绍(十九):内存操作函数
- 【学习笔记】【C语言】三目运算符
- Scala学习第五天:数组的基本操作,数组的进阶操作和多维数组
- C/C++学习之 C语言文件操作
- C语言逗号运算符和逗号表达式
- python学习笔记(五)文件操作、正则表达式
- 【MongoDB学习笔记13】MongoDB的更新(update)操作进阶