91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C之函數參數(三十九)

發布時間:2020-06-19 11:27:21 來源:網絡 閱讀:744 作者:上帝之子521 欄目:編程語言

        我們上節博文講了函數的意義,那么我們今天來講下函數參數函數參數在本質上與局部變量相同在棧上分配空間,函數參數的初始值是函數調用時的實參值。用下圖來實際說明

C之函數參數(三十九)

        函數參數的求值順序依賴于編譯器的實現,我們來看看下面代碼的輸出是什么?為什么呢?

#include <stdio.h>

int func(int i, int j)
{
    printf("i = %d, j = %d\n", i, j);
    
    return 0;
}

int main()
{
    int k = 1;
    
    func(k++, k++);
    
    printf("%d\n", k);
    
    return 0;
}

        我們理論上分析,func 函數先進行 k++,那么 i 就對應為 1,再次進行 k++,對應于 j 為 2。那么第 14 行應打印 i = 1, j = 2,。這時 k 為 3,所以第 16 行打印的值應為 3。我們來看看編譯結果是否如我們所分析的那樣,編譯結果如下

C之函數參數(三十九)

        我們看到 i 和 j 和我們所分析的正好相反,那么這是怎么回事呢?原來在gcc 編譯器中,函數參數的實現是從右向左進行操作的,并非是我們所想的從左向右進行計算的。我們再在 BCC 編譯器中進行編譯,看看結果是怎樣?

C之函數參數(三十九)

        那么我們看到在 BCC 編譯器中也是這樣實現的。函數參數的操作是從右向左的,在現代的編譯器中,基本上是按照從右向左的順序進行函數參數的操作的。在一些古老的編譯器中,也有從左向右的實現,這個的實現就依賴于具體的編譯器的實現了。

        下來我們來講一個 C 語言中的知識點:順序點!那么在程序中存在一定的順序點,順序點是指執行過程中修改變量值的最晚時刻在程序到達順序點的時候,之前所做的一切操作必須完成。那么 C 語言中的順序點都在那些時刻呢?a> 每個完整表達式結束時,即分號處;b> &&,||,?: 以及逗號表達式的每個參數計算之后;c> 函數調用時所有實參求值完成后(即進入函數之前)

        下面我們以代碼為例進行分析,代碼如下

#include <stdio.h>

int main()
{
    int k = 2;
    int a = 1;
    
    k = k++ + k++;
    
    printf("k = %d\n", k);
    
    if( a-- && a )
    {
        printf("a = %d\n", a);
    }
    
    return 0;
}

        我們看到第 8 行的進行兩次 k++ 的相加,我們分析結果應該為 5;第 12 行的 a-- 執行完之后 a 為 0,但是此時它和 a 相與之后條件仍然為真,所以 第14行應該打印出 a = 0;我么來看看結果是這樣嗎?

C之函數參數(三十九)

        那么我們看到我們分析的第一個是正確的,但是 a = 0 并沒有打印出來,我們再來看看 BCC 編譯器是多少

C之函數參數(三十九)

        我們看到竟然 k = 6,a = 0 依然沒有打印出來。我們再來看看 VS 編譯器

C之函數參數(三十九)

        我們進到反匯編看看它是怎么執行的

C之函數參數(三十九)

        我們看到它是這樣執行的,先是進行相加操作,這時的++操作被懸掛起來,程序看到;才意識到到了順序點了,所以執行完那兩次++操作,所以最后 k 的值為6。我們再來看看第14行怎么執行的

C之函數參數(三十九)

        我們看到它是執行完 a-- 后看到 && 操作便意識到順序點到了,便返回了。那么這時 a 的值已經變為 0 了,此時 if 語句條件為假,所以不會執行到它里面的打印語句。

        下來我們再來看看參數入棧的順序,函數參數的計算次序是依賴編譯器實現的。那么函數參數的入棧次序是如何確定的呢?這塊就涉及到里一個概念:調用約定。當函數調用發生時:a> 參數會傳遞給被調用的函數;b> 而返回值會被返回給函數調用者;調用約定描述參數如何傳遞到棧中以及棧的維護方式,參數傳遞順序,調用棧清理。

        調用約定是預定義的可理解為調用協議,調用約定通常用于庫調用和庫開發的時候。我們來看看一些常用的操作:a>從右到左依次入棧:__stfcall, __cdecl, __thiscall;b> 從左到右依次入棧:__pascall, __fastcall;那么我們一般的 C 程序開發遵循的就是上面的 __cdecl 這種方式的。

        那么我們如果要編寫一個計算平均數的函數,我們肯定首先想到的是下面這種

#include <stdio.h>

float average(int array[], int size)
{
    int i = 0;
    float avr = 0;
    
    for(i=0; i<size; i++)
    {
        avr += array[i];
    }
    
    return avr / size;
}

int main()
{
    int array[] = {1, 2, 3, 4, 5};
    
    printf("%f\n", average(array, 5));
    
    return 0;
}

        我們利用一個數組就完成這個功能,那么我們還得去定義一個數組。有什么辦法可以使我們不用定義數組就可以完成這個功能呢?答案就是我們可以利用可變參數的函數來實現這個功能。在 C 語言中可以定義參數可變的函數,參數可變函數的實現依賴于 stdarg.h 頭文件。我們得介紹幾個概念:a> va_list -- 參數集合;b> va_arg -- 取具體參數值;c> va_start -- 標識參數訪問的開始;d> va_end -- 標識參數訪問的結束

        下來我們來看看可變參數版的程序是怎樣實現的,代碼如下

#include <stdio.h>
#include <stdarg.h>

float average(int n, ...)
{
    va_list args;
    int i = 0;
    float sum = 0;
    
    va_start(args, n);
    
    for(i=0; i<n; i++)
    {
        sum += va_arg(args, int);
    }
    
    va_end(args);
    
    return sum / n;
}

int main()
{
    printf("%f\n", average(5, 1, 2, 3, 4, 5));
    printf("%f\n", average(4, 1, 2, 3, 4));
    
    return 0;
}

        我們在第 6 行定義了 args 參數,在第 10 行開始,14 行進行參數的相加,在 17 行結束。我們來看看第24, 25 行的這樣的定義可行嗎?來看看編譯結果

C之函數參數(三十九)

        結果已經正確實現了,這樣是不是很方便呢?我們可以隨時定義它的大小和內容。那么可變參數也有限制:a> 可變參數必須從頭到尾按照順序逐個訪問;b> 參數列表中至少要存在一個確定的命名參數;c> 可變參數函數無法確定實際存在的參數的數量,同樣也無法確定參數的實際類型,只能我們手動指定;注意:va_arg 中指定了錯誤的類型,那么結果是不可預測的!

        通過對函數參數的學習,總結如下:1、函數的參數在棧上分配空間;2、函數的實參并沒有固定的計算次序;3.順序點是 C 語言中變量修改的最晚時機;4、調用約定指定了函數參數的入棧順序以及棧的清理方式;5、可變參數的函數提供了一種函數設計技巧,提供了一種更方便的函數調用方式;6、可變參數必須順序的訪問,無法直接訪問中間的參數值。


        歡迎大家一起來學習 C 語言,可以加我QQ:243343083

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

伊春市| 遵义县| 隆安县| 邳州市| 大洼县| 翼城县| 临夏县| 枣强县| 越西县| 南召县| 平顺县| 弥勒县| 丽水市| 巨鹿县| 松桃| 台南市| 张家界市| 手游| 渑池县| 峨眉山市| 朝阳区| 涟水县| 邹平县| 资源县| 景德镇市| 普宁市| 晴隆县| 灵山县| 金堂县| 玛沁县| 阳江市| 青田县| 房产| 陆丰市| 洮南市| 来凤县| 农安县| 清镇市| 开鲁县| 凯里市| 车致|