C语言——if(0)之后的语句真的不会执行吗?
1、序
学过c语言的都知道,通常:If(0)之后的代码是不执行的,网上也有详细的说明。
1.1、形式:
if (表达式) { 语句... }
1.2、解释:
在执行if语句时,首先会计算表达式的值,如果表达式的值为零,语句不会执行,若非零,则执行语句。由此可见if (0) 表示不执行,if (1)表示要执行。if (x)根据x的值是否为0来决定是否执行,他等价于if (x != 0)。
if语句中的条件无论是什么最终都要转换成一个布尔值,因此,
1.3、举个例子
if(x)相当于if(x != 0)
对于x为指针,相当于if(x != NULL)
而if(1)
因为1为整型,相当于if(1 != 0)
1肯定不等于0,所以就相当于一定执行if里面的语句.
而if(0)相当于if(0 != 0)
这肯定不成立,所以一定不会执行if中的语句.
x == 1,x != 1也是表达式,称为关系表达式,在C语言里,关系成立,表达式的值为1,不成立则为0,所以1>2的值为0,1!=2的值为1。C语言老师应该提到过,x大于2小于5不能写成 2 < x < 5,因为这货会被解释为(2 < x) < 5,无论x取多少,这个式子的值恒为1(根据刚刚说的应该能理解为什么了吧)。
x=1也是一种表达式,叫赋值表达式,他的值就是等号右边的式子的值,也就是1。所以if(x=1)无论x原来为多少if语句都会成立,并且会将x的值改写为1,和if(x==1)有着非常大的区别(那么,if(x=0)呢?),正是x=1这个表达式是有值的,C语言才允许if(x=1)这种写法,一些语言里x=1这个式子是没有值的,只是将x赋值为1,这样写就会报错,如Java。C语言代码里出现if(x=1)一般情况下是你写错了,可能想表达的是if(x==1)。现在的编译器里,if(x=1)一般都会有警告提示你,可能写错了。
总之,在C语言了里,像if,for,while这些语句本质上都是通过求出括号里表达式的是否为0来决定运行流程的,所以像if(scanf("%d",&a))这种代码也是可以理解了的。
上面的文字应该不难理解,过了二级C语言的同学应该都能理解了。
然而。有例外,近期在微信群中看到大佬们提到了Clifford's Device,由于一个比较冷门的c语言技巧,趁此学习下。
2、主要参考资料
1、菜鸟教程:C 库函数 – strtol() | 菜鸟教程 (runoob.com)和strtol - C++ Reference (cplusplus.com)以及C++ Shell (cpp.sh)在线运行网站
2、Clifford's Homepage - Fun with Program Code。其主页:Claire Wolf (clifford.at),是一个大佬的主页,肯定是比较牛逼的,资料是英文的,不难看懂,看不懂可以谷歌翻译。
这位大佬在文章也提到过Duff's Device,这个是比较出名的,他自己想出来一个switch case的代码框架(暂且如此称呼)。经过gcc编译运行,语法没有错。
3、goto版本代码
goto是一个关键字,可以在函数内直接跳转到某个label处再执行,在某些场合是比较适合的,linux中也有用到(linus也是大神~)贴代码之前,上一个库函数的c语言例子先热热身。
C 库函数 - strtol()
包含于标准库 - <stdlib.h>
——描述
long int strtol(const char *str, char **endptr, int base)
把参数 str 所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型),base 必须介于 2 和 36(包含)之间,或者是特殊值 0。
——声明
下面是 strtol() 函数的声明。
long int strtol(const char *str, char **endptr, int base)
——参数
str -- 要转换为长整数的字符串。
endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中数值后的下一个字符。
base -- 基数,必须介于 2 和 36(包含)之间,或者是特殊值 0。
——返回值
该函数返回转换后的长整数,如果没有执行有效的转换,则返回一个零值。
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /* strtol example */ #include <stdio.h> /* printf */ #include <stdlib.h> /* strtol */ int main () { char szNumbers[] = "2001 60c0c0 -1101110100110100100000 0x6fffff" ; char * pEnd; long int li1, li2, li3, li4; li1 = strtol (szNumbers,&pEnd,10); li2 = strtol (pEnd,&pEnd,16); li3 = strtol (pEnd,&pEnd,2); li4 = strtol (pEnd,NULL,0); printf ( "The decimal equivalents are: %ld, %ld, %ld and %ld.\n" , li1, li2, li3, li4); return 0; } |
编译运行后:
The decimal equivalents are: 2001, 6340800, -3624224 and 7340031.
接下去,看看大佬的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #include <stdio.h> #include <stdlib.h> #include <string.h> int main ( int argc, char ** argv) { int num; if (argc != 3) { fprintf (stderr, "Usage: %s {BIN|OCT|DEC|HEX|STR} {ARG}\n" , argv[0]); return 1; } if (! strcmp (argv[1], "BIN" ) ) { num = strtol (argv[2], NULL, 2); goto number_mode; } else if (! strcmp (argv[1], "OCT" ) ) { num = strtol (argv[2], NULL, 8); goto number_mode; } else if (! strcmp (argv[1], "DEC" ) ) { num = strtol (argv[2], NULL, 10); goto number_mode; } else if (! strcmp (argv[1], "HEX" ) ) { num = strtol (argv[2], NULL, 16); goto number_mode; } else if (! strcmp (argv[1], "STR" ) ) { printf ( "Called with string argument: '%s'\n" , argv[2]); } else { printf ( "Called unsupported mode: '%s'\n" , argv[1]); } /* Clifford's Device */ if (0) { number_mode: printf ( "Called with numeric argument: %d\n" , num); } return 0; } |
运行后:
1 2 3 4 5 6 7 8 9 10 | 编译: gcc .\Clifford-Device- goto .c -o .\Clifford-Device- goto .exe 无参数运行,提示报错 .\Clifford-Device- goto .exe Usage: Clifford-Device- goto .exe {BIN|OCT|DEC|HEX|STR} {ARG} 带十六进制参数 .\Clifford-Device- goto .exe HEX 0x1234 Called with numeric argument: 4660 0x1234的确=4660 代码测试完成! |
这个代码应该不难理解了,具体可以实际上机测试体验下。
4、switch版本代码
这里使用了if(0),直接运行的效果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | #include <stdio.h> #define IF_DEF 1 //int main (int argc) int main ( void ) { char * num; int argc_test; for ( int i = 0; i < 7; i++) { argc_test = i; #if IF_DEF == 1 printf ( "if (0)\n" ); switch (argc_test - 1) { if (0) { case 0: num = "zero" ; printf ( "==0\n" ); } if (0) { case 2: num = "two" ; printf ( "==2\n" ); } if (0) { case 3: num = "three" ; printf ( "==3\n" ); } if (0) { case 4: num = "four" ; printf ( "==4\n" ); } if (0) { case 5: num = "five" ; printf ( "==5\n" ); } if (0) { default : num = "many" ; printf ( "==...\n" ); } printf ( "Called with %s arguments.\n" , num); break ; case 1: printf ( "Called with one argument.\n" ); } #else printf ( "no if (0)\n" ); switch (argc_test - 1) { //if (0) { case 0: num = "zero" ; printf ( "==0\n" ); } //if (0) { case 2: num = "two" ; printf ( "==2\n" ); } //if (0) { case 3: num = "three" ; printf ( "==3\n" ); } //if (0) { case 4: num = "four" ; printf ( "==4\n" ); } //if (0) { case 5: num = "five" ; printf ( "==5\n" ); } //if (0) { default : num = "many" ; printf ( "==...\n" ); } printf ( "Called with %s arguments.\n" , num); break ; case 1: printf ( "Called with one argument.\n" ); } #endif } return 0; } |
运行后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | if (0) ==... Called with many arguments. if (0) ==0 Called with zero arguments. if (0) Called with one argument. if (0) ==2 Called with two arguments. if (0) ==3 Called with three arguments. if (0) ==4 Called with four arguments. if (0) ==5 Called with five arguments. |
部分代码已经做了修改,便于学习。
是不是很疑惑?为何没有break,也没有被fall through,经过咨询大佬,原来switch-case类似于goto-label,难怪其效率是高于if() {} else if() {} else {}结构的。另外if(0)可以防止被fall through 对吧,等同于添加了break。
这下应该真相大白了,原来c语言还有这个操作,难以想象,具体的思想可以看原版英文。平时使用还是老老实实的按规范写代码,毕竟项目是需要维护的,而不是秀技巧的。
近期开通了公众号,也一并告知下,
扫描关注:
个人公众号:嵌入式软件自留地
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2019-05-03 g++编译器的使用(转载)
2019-05-03 五一学习记录(一)
2019-05-03 列几本侯捷老师推荐的书:
2019-05-03 离线安装 Visual Studio Express 而不下载整个镜像文件的方法(转载)
2012-05-03 c8051f 的IO配置方法及原则
2012-05-03 c8051f交叉开关