<var id="fnfpo"><source id="fnfpo"></source></var>
<rp id="fnfpo"></rp>

<em id="fnfpo"><object id="fnfpo"><input id="fnfpo"></input></object></em>
<em id="fnfpo"><acronym id="fnfpo"></acronym></em>
  • <th id="fnfpo"><track id="fnfpo"></track></th>
  • <progress id="fnfpo"><track id="fnfpo"></track></progress>
  • <tbody id="fnfpo"><pre id="fnfpo"></pre></tbody>

  • x
    x

    C語言的那些小秘密之變參函數的實現

    發布時間:2016-2-19 09:08    發布者:designapp
    關鍵詞: C語言 , 函數
    在學習C語言的過程中我們可能很少會去寫變參函數,印象中大學老師好像也沒有提及過,但我發現變參函數的實現很巧妙,所以還是特地在此分析下變參函數的實現原理。無需標準C的支持,我們自己寫代碼來實現。
    先來看看一個實現代碼:
    #include
    #define va_list void*
    #define va_arg(arg, type) *(type*)arg; arg = (char*)arg + sizeof(type);
    #define va_start(arg, start) arg = (va_list)(((char*)&(start)) + sizeof(start))
    int sum(int nr, ...)
    {
    int i = 0;
    int result = 0;
    va_list arg = NULL;
    va_start(arg, nr);
    for(i = 0; i


    #define va_list void*通過這句代碼我們實現了定義va_list是一個指針,參數類型不定,它可以指向任意類型的指針。為了讓arg指向第一個可變參數,我們用nr的地址加上nr的數據類型大小就行了,采用如下的定義可以實現。
    #define va_start(arg, start) arg = (va_list)(((char*)&(start)) + sizeof(start)) 。
    通過(((char*)&(start)) + sizeof(start)) 可以得到第一個可變參數的地址,再將其強制轉換為va_list類型。
    成功取出了第一個可變參數后,接下來的任務就是繼續取出可變參數,方法跟上面求第一個可變參數的方法一樣,通過arg = (char*)arg + sizeof(type);來實現讓arg指向下一個可變參數,type為可變參數的類型,通過這種方法可以一一取出可變參數。
    在這里順便給出上面實現代碼的匯編代碼,有興趣的可以讀讀,加深下對于底層匯編代碼的閱讀能力。
    .file "varargs.c"
    .text
    .globl sum
    .type sum, @function
    sum:
    pushl %ebp
    movl %esp, %ebp
    subl $16, %esp
    movl $0, -4(%ebp)
    movl $0, -8(%ebp)
    movl $0, -12(%ebp)
    leal 12(%ebp), %eax
    movl %eax, -12(%ebp)
    movl $0, -4(%ebp)
    jmp .L2
    .L3:
    movl -12(%ebp), %eax
    movl (%eax), %eax
    addl %eax, -8(%ebp)
    addl $4, -12(%ebp)
    addl $1, -4(%ebp)
    .L2:
    movl 8(%ebp), %eax
    cmpl %eax, -4(%ebp)
    jl .L3
    movl -8(%ebp), %eax
    leave
    ret
    .size sum, .-sum
    .section .rodata
    .LC0:
    .string "%d\n"
    .text
    .globl main
    .type main, @function
    main:
    pushl %ebp
    movl %esp, %ebp
    andl $-16, %esp
    subl $32, %esp
    movl $100, 16(%esp)
    movl $100, 12(%esp)
    movl $100, 8(%esp)
    movl $100, 4(%esp)
    movl $4, (%esp)
    call sum
    movl $.LC0, %edx
    movl %eax, 4(%esp)
    movl %edx, (%esp)
    call printf
    movl $200, 12(%esp)
    movl $200, 8(%esp)
    movl $200, 4(%esp)
    movl $3, (%esp)
    call sum
    movl $.LC0, %edx
    movl %eax, 4(%esp)
    movl %edx, (%esp)
    call printf
    movl $0, %eax
    leave
    ret
    .size main, .-main
    .ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
    .section .note.GNU-stack,"",@progbits樹莓派文章專題:樹莓派是什么?你不知道樹莓派的知識和應用
                                    
                   
    其中有幾條指令在此講解下。
    leave指令所做的操作相當于如下兩條指令:
    movl %ebp, %esp
    popl %ebp
    ret指令所做的操作相當于如下指令:
    pop %eip
    如果有對AT&T匯編語法規則不懂的,可以看看我前面寫的那篇文章。
    到這兒為止是乎應該是說結束的時候了,但是細心的讀者可能發現了一個問題,就是我們在最初給出的代碼部分有一句紅色標記的代碼,如下:
    #define va_start(arg, start) arg = (va_list)(((char*)&(start)) + sizeof(start))
    為什么要把這句代碼單獨拿出來講解呢,肯定是有原因的,因為((char*)&(start)) +sizeof(start)這句代碼的特殊性在于使用了(char*)進行強制轉換,在這里為什么不使用(int*)進行強制轉換呢,如改為如下代碼:
    #include
    #include
    #define va_list void*
    #define va_arg(arg, type) *(type*)arg; arg = (char*)arg + sizeof(type);
    #define va_start(arg, start) arg = (va_list)(((int*)&(start)) + sizeof(start)) //修改為(int*)
    int sum(int nr, ...)
    {
    int i = 0;
    int result = 0;
    va_list arg = NULL;
    va_start(arg, nr);
    for(i = 0; i


    顯然運行結果是錯誤的,為什么會出現這樣的錯誤呢,我們暫且不分析,先來看看我們接下來做的修改:
    #include
    #include
    #define va_list void*
    #define va_arg(arg, type) *(type*)arg; arg = (char*)arg + sizeof(type);
    #define va_start(arg, start) arg = (va_list)(((int*)&(start)) + sizeof(start)/4) //注意對比紅色部分的變化
    int sum(int nr, ...)
    {
    int i = 0;
    int result = 0;
    va_list arg = NULL;
    va_start(arg, nr);
    for(i = 0; i


    運行結果正確。
    現在來分析下為什么會出現這兩種結果呢,看看下面我給出的這個圖解和代碼應該就能夠很清楚的理解為什么會出現以上的兩種運行結果了。


    代碼如下:
    #include
    int main()
    {
    int a = 12;
    int *p_int = &a;
    char *p_char = (char*)&a;
    printf( "%d \t", sizeof(char));
    printf( "%d \t", sizeof(int));
    printf( "%d \t", p_int+1);
    printf( "%d \t", p_char+1);
    return 0;
    }
    運行結果為:


    修改以上紅色部分的代碼,得到如下代碼:
    #include
    int main()
    {
    int a = 12;
    int *p_int = &a;
    char *p_char = (char*)&a;
    printf( "%d \t", sizeof(char));
    printf( "%d \t", sizeof(int));
    printf( "%d \t", p_int+1);
    printf( "%d \t", p_char+4);
    return 0;
    }
    注意對比前后代碼的變化部分!!!
    運行結果如下:


    首先看看給出的圖,int指針所指向的單元占有四個字節的空間,而char指針所指向的單元只占有一個字節的空間。所以如果是整形指針想要取下一個參數,只需加1,但是如果是char指針,如果當前參數是int型,那么想要取下一個參數就要加4才能實現。但是值得注意的是,int*和char*所占的存儲單元都是4個字節,這是由我們所使用的32位計算機本身確定的。為了加深大家的印象,特地給出如下代碼:
    #include
    int main()
    {
    int a = 12;
    int *p_int = &a;
    char *p_char = (char*)&a;
    printf( "%d \t", sizeof(char*));
    printf( "%d \t", sizeof(int*));
    return 0;
    }
    運行結果如下:


    到此為止就是真的該結束了,總不能沒完沒了的講下去吧,呵呵……
    很多代碼僅僅是修改了一點,我都貼出了完整的代碼,是希望你在閱讀的過程中能直接copy過去,看看運行效果,加深下印象。還是那句話,C語言博大精深,我還是C語言菜鳥,以上內容難免有錯。
    本文地址:http://www.portaltwn.com/thread-160943-1-1.html     【打印本頁】

    本站部分文章為轉載或網友發布,目的在于傳遞和分享信息,并不代表本網贊同其觀點和對其真實性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問題,我們將根據著作權人的要求,第一時間更正或刪除。
    您需要登錄后才可以發表評論 登錄 | 立即注冊

    廠商推薦

    • Microchip視頻專區
    • 你仿真過嗎?使用免費的MPLAB Mindi模擬仿真器降低設計風險
    • 深度體驗Microchip自動輔助駕駛應用方案——2025巡展開啟報名!
    • 更佳設計的解決方案——Microchip模擬開發生態系統
    • 我們是Microchip
    • 貿澤電子(Mouser)專區

    相關視頻

    關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯系我們
    電子工程網 © 版權所有   京ICP備16069177號 | 京公網安備11010502021702
    快速回復 返回頂部 返回列表
    精品一区二区三区自拍图片区_国产成人亚洲精品_亚洲Va欧美va国产综合888_久久亚洲国产精品五月天婷