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。
——返回值
該函數返回轉換后的長整數,如果沒有執行有效的轉換,則返回一個零值。
測試代碼:
/* 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.
接下去,看看大佬的代碼
#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; }
運行后:
編譯: 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),直接運行的效果如下:
#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; }
運行后:
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語言還有這個操作,難以想象,具體的思想可以看原版英文。平時使用還是老老實實的按規范寫代碼,畢竟項目是需要維護的,而不是秀技巧的。
近期開通了公眾號,也一並告知下,
掃描關注:
個人公眾號:嵌入式軟件自留地