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。

——返回值

該函數返回轉換后的長整數,如果沒有執行有效的轉換,則返回一個零值。

測試代碼:

/* 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語言還有這個操作,難以想象,具體的思想可以看原版英文。平時使用還是老老實實的按規范寫代碼,畢竟項目是需要維護的,而不是秀技巧的。

 

近期開通了公眾號,也一並告知下,

掃描關注:

個人公眾號:嵌入式軟件自留地


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM