C 語言解析:void 意義、NULL 意義 | main 函數調用、函數返回值意義 | 臨時變量的產生

C 語言解析:void 意義、NULL 意義 | main 函數調用、函數返回值意義 | 臨時變量的產生

Overview of Content

這裡主要來看看 C 語言的細節部分

本文將探討在C語言中 void 類型 的意義以及 NULL 的意義。我們將介紹 void 類型的用法和指針,以及 NULL 在不同上下文中的差異,包括與 '\0'、'0'、0、NULL 的比較

接著,我們將討論在C語言中傳回值的意義,包括 main 函數的標準寫法、接收參數、對 main 函數傳回值的意義。

我們也會探討在C語言中函數 function 傳回數值的習慣和規範

最後,我們將關注C語言中產生的臨時變量,包括強制類型轉換和臨時變量的運算。透過學習這些內容,讀者將能夠更好地理解C語言中的 void 類型、NULL 值以及傳回值的含義和使用方法


void 意義

void 是一種「類型」,稱為「無類型」,可理解成尚未定義的類型 (類似 Java 的 Object 類)

在指定類型後,編譯器才會按照該類型的解析方式去讀取該區塊的記憶體

void 指標

A. void 指標代表一段尚未確認(尚未定義類型)的數據,可以透過強制轉型,告訴編譯器該如何解析該段記憶體數據


typedef struct MyClz {
    int a;
    short b;
    char c;
} MyClz_t;

void test_void() {
    MyClz_t clz;

    clz.a = 10000;
    clz.b = 100;
    clz.c = 'a';

    void *p = &clz;

    printf("analysis by MyClz_t: %d\n", ((MyClz_t *)p)->a);
    printf("analysis by MyClz_t: %d\n", ((MyClz_t *)p)->b);
    printf("analysis by MyClz_t: %c\n", ((MyClz_t *)p)->c);

}

B. 較常見的是使用 malloc API(這個 API 來自於 C 語言提供的標準庫,用來動態申請記憶體空間),該 API 就是返回一個 void * 指標;測試範例如下


#include <stdlib.h>

typedef struct MyClz {
    int a;
    short b;
    char c;
} MyClz_t;

void test_void_lib() {
    void *p = malloc(sizeof(MyClz_t));

    MyClz_t *pClz = (MyClz_t *)p;

    pClz->a = 222333;
    pClz->b = 111;
    pClz->c = 'z';

    printf("analysis by MyClz_t: %d\n", pClz->a);
    printf("analysis by MyClz_t: %d\n", pClz->b);
    printf("analysis by MyClz_t: %c\n", pClz->c);

    free(pClz);
}

NULL 意義

NULL 並不是 C 語言的關鍵字,其定義如下,NULL 在 C/C 是不同的意思


#ifdef _cplusplus
#define NULL     0                // C++ 定義
#else
#define NULL     (void *)0        // C 定義
#endif

這裡我們來解釋一下 C 語言的 NULL,它是一個 1. void* 類型的指標2. 0 是指向位置 0x00000000 的記憶體空間

● 大部分 CPU 中的 0x00000000 記憶體是特殊段,不可隨意訪問


'\0''0'0NULL 差異

目標類型說明
'\0'charASCII 的 0,在 C 中作為字符串的結尾
'0'charASCII 的 48
0數字就是 0
NULLvoid*也是 0,不過它是地址 0x00000000 的意思

返回值意義

main 函數:標準寫法

● main 是 C 語言的起始點,它有以下幾種寫法 (C99 版本),這幾種寫法都是可以的

A. main 入口函數 - 不帶參數


int main(void) {
    
 printf("Hello World");
    
    return 0;
}

B. main 入口函數 - 帶參數argc 是數量,一個是參數指標;預設參數是該檔案的路徑


// 指標數組

int main(int argc, char *argv[]) 
    printf("argument count: %d\n", argc);

    // 以下兩種寫法相同
    printf("argument value: %s\n", *argv);
    printf("argument value: %s\n", argv[0]);
    return 0;
}

以下另外一種寫法(二重指標)也可以,這兩種寫法都可以得到同樣的結果


// 二重指標

int main(int argc, char **argv) 
    printf("argument count: %d\n", argc);

    // 以下兩種寫法相同
    printf("argument value: %s\n", *argv);
    printf("argument value: %s\n", argv[0]);
    return 0;
}

● 編譯出 .out 執行檔後,在呼叫時需要傳參數就可以直接將參數加在後面


gcc return_test.c -o return_test.out

./return_test.out test 12345

如果有多個參數,那參數間要使用 空格 隔開

誰調用 main 函數 - main 返回值

誰調用 main 函數:這分為 兩種方式 (系統決定)

A. 無系統 MCU 系列:是由匯編 (組合) 語言來調用,先調用加載函數庫,初始化 Stack、Heap,最後會調用 main 函數

B. 有系統 PC 系列:由系統來調用,透過 fork 創建虛擬記憶體的新分頁、exec 覆蓋並執行 main 函數

main 函數的返回值

會由調用它(創建這個程序,可能是 PC 系統,也可能是 MCU)的 Parent Process 接收,而接收到返回值後如何操作則是 Parent Process 決定

C 語言 - 返回數的習慣

● C 語言 - 返回數的習慣有兩種 !(並非強制,建議在使用 API 時還是要詳細看看文件說明)

函數類型返回 0 的含意返回非 0 的含意
判斷函數成功失敗
操作函數失敗成功

A. 操作函數:操作類型的函數,返回 0 代表成功,非 0(像是返回 -1)則是失敗


void use_string_cmp() {
    char *p = "Hello";
    char *pa = "Hello";

    // 呼叫操作類型函數 show_on_console
    int show_result = show_on_console(p, pa);

    if(show_result == 0) {
        printf("Success\n");
    } else {
        printf("Fail\n");
    }
}

B. 判斷函數:邏輯判斷類型的函數,返回 0 代表判斷失敗,非 0(像是返回 1)則是成功


// 邏輯判類型的函數
int is_litte_endian() {
    union Test t;
    t.a = 1;

    if(t.b == 1) {
        printf("litte_endian\n");
        return 1;     // 系統為小端
    } else {
        printf("big_endian\n");
        return 0;     // 系統為大端
    }
}

C 語言產生的臨時變量

臨時變量由 C 語言自己提供,並不會顯示顯現 (匿名),它們的顯示時機如下

強制轉型

● 在強制轉型成會產生一個臨時匿名變量,強制轉型的案例如下:


void force_change() {
    float f = 10.023;
    // 這裡會產生臨時變量 `x`
    int a = f;          

    printf("a is %d\n");
}

A. 產生臨時變量 x:就像是上面案例… 將 10.023 的整數部分 10 存起來

B. 將臨時變量 x 賦予 a

C. 在離開函數時,銷毀臨時變量 x

臨時變量運算

● 另外一個產生臨時變量的時機就是在運算時,運算的案例如下:


void cal() {
    int a = 10;
    // 這裡會產生臨時變量 `x`
    float b = a / 3;

    printf(" is %d\n", b);
}

A. 產生臨時變量 x:就像是上面案例… 將 10 / 3 的結果 3.33333 存起來

B. 將臨時變量 x 賦予 b

C. 在離開函數時,銷毀臨時變量 x


更多的 C 語言相關文章

關於 C 語言的應用、研究其實涉及的層面也很廣闊,但主要是有關於到系統層面的應用(所以 C 語言又稱之為系統語言),為了避免文章過長導致混淆重點,所以將文章係分成如下章節來幫助讀者更好地從不同的層面去學習 C 語言

編譯器、系統開念

編譯器、系統開念:是學習完 C 語言的基礎(或是有一定的程度)之後,從編譯器以及系統的角度重新檢視 C 語言的一些細節

C 語言與系統開發

C 語言與系統開發:在這裡會說明 C 語言的實際應用,以及系統為 C 語言所提供的一些函數、庫... 等等工具,看它們是如何實現、應用

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響