最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語(yǔ)言如何實(shí)現(xiàn)可變參數(shù)詳解

 更新時(shí)間:2021年07月26日 11:41:28   作者:東垂小夫  
這種可變參數(shù)可以說是C語(yǔ)言一個(gè)比較難理解的部分,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言如何實(shí)現(xiàn)可變參數(shù)的相關(guān)資料,需要的朋友可以參考下

可變參數(shù)

可變參數(shù)是指函數(shù)的參數(shù)的數(shù)據(jù)類型和數(shù)量都是不固定的。

printf函數(shù)的參數(shù)就是可變的。這個(gè)函數(shù)的原型是:int printf(const char *format, ...)。

用一段代碼演示printf的用法。

// code-A
#include <stdio.h>
int main(int argc, char **argv)
{
   printf("a is %d, str is %s, c is %c\n", 23, "Hello, World;", 'A');
   printf("T is %d\n", 78);
   return 0;
}

在code-A中,第一條printf語(yǔ)句有4個(gè)參數(shù),第二條printf語(yǔ)句有2個(gè)參數(shù)。顯然,printf的參數(shù)是可變的。

實(shí)現(xiàn)

代碼

code-A

先看兩段代碼,分別是code-A和code-B。

// file stack-demo.c

#include <stdio.h>

// int f(char *fmt, int a, char *str);
int f(char *fmt, ...);
int f2(char *fmt, void *next_arg);
int main(int argc, char *argv)
{
        char fmt[20] = "hello, world!";
        int a = 10;
        char str[10] = "hi";
        f(fmt, a, str);
        return 0;
}

// int f(char *fmt, int a, char *str)
int f(char *fmt, ...)
{
        char c = *fmt;
        void *next_arg = (void *)((char *)&fmt + 4);
        f2(fmt, next_arg);
        return 0;
}


int f2(char *fmt, void *next_arg)
{
        printf(fmt);
        printf("a is %d\n", *((int *)next_arg));
        printf("str is %s\n", *((char **)(next_arg + 4)));

        return 0;
}

編譯執(zhí)行,結(jié)果如下:

# 編譯
[root@localhost c]# gcc -o stack-demo stack-demo.c -g -m32
# 反匯編并把匯編代碼寫入dis-stack.asm中
[root@localhost c]# objdump -d stack-demo>dis-stack.asm
[root@localhost c]# ./stack-demo
hello, world!a is 10
str is hi

code-B

// file stack-demo.c

#include <stdio.h>

// int f(char *fmt, int a, char *str);
int f(char *fmt, ...);
int f2(char *fmt, void *next_arg);
int main(int argc, char *argv)
{
        char fmt[20] = "hello, world!";
        int a = 10;
        char str[10] = "hi";
     char str2[10] = "hello";
        f(fmt, a, str, str2);
        return 0;
}

// int f(char *fmt, int a, char *str)
int f(char *fmt, ...)
{
        char c = *fmt;
        void *next_arg = (void *)((char *)&fmt + 4);
        f2(fmt, next_arg);
        return 0;
}


int f2(char *fmt, void *next_arg)
{
        printf(fmt);
        printf("a is %d\n", *((int *)next_arg));
        printf("str is %s\n", *((char **)(next_arg + 4)));
     printf("str2 is %s\n", *((char **)(next_arg + 8)));

        return 0;
}

編譯執(zhí)行,結(jié)果如下:

# 編譯
[root@localhost c]# gcc -o stack-demo stack-demo.c -g -m32
# 反匯編并把匯編代碼寫入dis-stack.asm中
[root@localhost c]# objdump -d stack-demo>dis-stack.asm
[root@localhost c]# ./stack-demo
hello, world!a is 10
str is hi
str2 is hello

分析

在code-A中,調(diào)用f的語(yǔ)句是f(fmt, a, str);;在code-B中,調(diào)用f的語(yǔ)句是f(fmt, a, str, str2);。

很容易看出,int f(char *fmt, ...);就是參數(shù)可變的函數(shù)。

關(guān)鍵語(yǔ)句

實(shí)現(xiàn)可變參數(shù)的關(guān)鍵語(yǔ)句是:

char c = *fmt;
void *next_arg = (void *)((char *)&fmt + 4);
printf("a is %d\n", *((int *)next_arg));
printf("str is %s\n", *((char **)(next_arg + 4)));
printf("str2 is %s\n", *((char **)(next_arg + 8)));
  • &fmt是第一個(gè)參數(shù)的內(nèi)存地址。
  • next_arg是第二個(gè)參數(shù)的內(nèi)存地址。
  • next_arg+4、next_arg+8分別是第三個(gè)、第四個(gè)參數(shù)的內(nèi)存地址。

為什么

內(nèi)存地址的計(jì)算方法

先看一段偽代碼。這段偽代碼是f函數(shù)的對(duì)應(yīng)的匯編代碼。假設(shè)f有三個(gè)參數(shù)。當(dāng)然f也可以有四個(gè)參數(shù)或2個(gè)參數(shù)。我們用三個(gè)參數(shù)的情況來觀察一下f。

f:

 ; 入棧ebp

 ; 把ebp設(shè)置為esp

 

 ; ebp + 0 存儲(chǔ)的是 eip,由call f入棧

 ; ebp + 4 存儲(chǔ)的是 舊ebp

 ; 第一個(gè)參數(shù)是 ebp + 8

 ; 第二個(gè)參數(shù)是 ebp + 12

 ; 第三個(gè)參數(shù)是 ebp + 16

 

 ; 函數(shù)f的邏輯

 

 ; 出棧ebp。ebp恢復(fù)成了剛進(jìn)入函數(shù)之前的舊ebp

 ; ret

調(diào)用f的偽代碼是:

; 入棧第三個(gè)參數(shù)

; 入棧第二個(gè)參數(shù)

; 入棧第一個(gè)參數(shù)

; 調(diào)用f,把eip入棧

在匯編代碼中,第一個(gè)參數(shù)的內(nèi)存地址很容易確定,第二個(gè)、第三個(gè)還有第N個(gè)參數(shù)的內(nèi)存地址也非常容易確定。無法是在ebp的基礎(chǔ)上增加特定長(zhǎng)度而已。

可是,我們只能確定,必定存在第一個(gè)參數(shù),不能確定是否存在的二個(gè)、第三個(gè)還有第N個(gè)參數(shù)。沒有理由使用一個(gè)可能不存在的參數(shù)作為參照物、并且還要用它卻計(jì)算其他參數(shù)的地址。

第一個(gè)參數(shù)必定存在,所以,我們用它作為確定其他參數(shù)的內(nèi)存地址的參照物。

內(nèi)存地址

在f函數(shù)的C代碼中,&fmt是第一個(gè)參數(shù)占用的f的棧的元素的內(nèi)存地址,換句話說,是一個(gè)局部變量的內(nèi)存地址。

局部變量的內(nèi)存地址不能作為函數(shù)的返回值,卻能夠在本函數(shù)執(zhí)行結(jié)束前使用,包括在本函數(shù)調(diào)用的其他函數(shù)中使用。這就是在f2中仍然能夠使用fmt計(jì)算出來的內(nèi)存地址的原因。

難點(diǎn)

當(dāng)參數(shù)是int類型時(shí),獲取參數(shù)的值使用*(int *)(next_arg)。

當(dāng)參數(shù)是char str[20]時(shí),獲取參數(shù)的值使用*(char **)(next_arg + 4)。

為什么不直接使用next_arg、(next_arg + 4)呢?

分析*(int *)(next_arg)。

在32位操作系統(tǒng)中,任何內(nèi)存地址的值看起來都是一個(gè)32位的正整數(shù)。可是這個(gè)正整數(shù)的值的類型并不是unsigned int,而是int *。

關(guān)于這點(diǎn),我們可以在gdb中使用ptype確認(rèn)一下。例如,有一小段代碼int *a;*a = 5;,執(zhí)行ptype a,結(jié)果會(huì)是int *。

next_arg只是一個(gè)正整數(shù),損失了它的數(shù)據(jù)類型,我們需要把數(shù)據(jù)類型補(bǔ)充進(jìn)來。我們能夠把這個(gè)操作理解成”強(qiáng)制類型轉(zhuǎn)換“。

至于*(int *)(next_arg)前面的*,很容易理解,獲取一個(gè)指針指向的內(nèi)存中的值。

用通用的方式分析*(char **)(next_arg+4)。

  1. 因?yàn)槭堑谌齻€(gè)參數(shù),因此next_arg+4。
  2. 因?yàn)榈谌齻€(gè)參數(shù)的數(shù)據(jù)類型是char str[20]。根據(jù)經(jīng)驗(yàn),char str[20]對(duì)應(yīng)的指針是char *。
  3. 因?yàn)閚ext_arg+4只是函數(shù)的棧的元素的內(nèi)存地址,在目標(biāo)元素中存儲(chǔ)的是一個(gè)指針。也就是說,next_arg+4是一個(gè)雙指針類型的指針。它最終又指向字符串,根據(jù)經(jīng)驗(yàn),next_arg+4的數(shù)據(jù)類型是char **。沒必要太糾結(jié)這一點(diǎn)。自己寫一個(gè)簡(jiǎn)單的指向字符串的雙指針,使用gdb的ptype查看這種類型的數(shù)據(jù)類型就能驗(yàn)證這一點(diǎn)。
  4. 最前面的*,獲取指針指向的數(shù)據(jù)。

給出一段驗(yàn)證第3點(diǎn)的代碼。

char str[20] = "hello";
char *ptr = str;
// 使用gdb的ptype 打印 ptype &ptr

打印結(jié)果如下:

Breakpoint 1, main (argc=1, argv=0xffffd3f4) at point.c:13
13  char str7[20] = "hello";
(gdb) s
14  char *ptr = str7;
(gdb) s
19  int b = 7;
(gdb) p &str
$1 = (char **) 0xffffd2fc

優(yōu)化

在code-A和code-B中,我們?nèi)斯じ鶕?jù)參數(shù)的類型來獲取參數(shù),使用*(int *)(next_arg)或*(char **)(next_arg + 4)。

庫(kù)函數(shù)printf顯然不是人工識(shí)別參數(shù)的類型。

這個(gè)函數(shù)的第一個(gè)參數(shù)中包含%d、%x、%s等占位符。遍歷第一個(gè)參數(shù),識(shí)別出%d,就用*(int *)next_arg替換%d。識(shí)別出

%s,就用*(char **)next_arg。

實(shí)現(xiàn)了識(shí)別占位符并且根據(jù)占位符選擇指針類型的功能,就能實(shí)現(xiàn)一個(gè)完成度很高的可變參數(shù)了。

總結(jié)

到此這篇關(guān)于C語(yǔ)言如何實(shí)現(xiàn)可變參數(shù)的文章就介紹到這了,更多相關(guān)C語(yǔ)言可變參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解C++中vector的理解以及模擬實(shí)現(xiàn)

    詳解C++中vector的理解以及模擬實(shí)現(xiàn)

    vector是表示可變大小數(shù)組的序列容器。這篇文章主要為大家詳細(xì)介紹了vector的理解以及模擬實(shí)現(xiàn),文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2023-03-03
  • C語(yǔ)言實(shí)現(xiàn)高精度加法的示例代碼

    C語(yǔ)言實(shí)現(xiàn)高精度加法的示例代碼

    高精度的本質(zhì)是將數(shù)字以字符串的形式讀入,然后將每一位分別存放入int數(shù)組中,通過模擬每一位的運(yùn)算過程,來實(shí)現(xiàn)最終的運(yùn)算效果,下面我們就來看看如何通過C語(yǔ)言實(shí)現(xiàn)高精度加法吧
    2023-11-11
  • 詳解C++11原子類型與原子操作

    詳解C++11原子類型與原子操作

    這篇文章主要介紹了C++11原子類型與原子操作的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下
    2020-08-08
  • Cocos2d-x人物動(dòng)作類實(shí)例

    Cocos2d-x人物動(dòng)作類實(shí)例

    這篇文章主要介紹了Cocos2d-x人物動(dòng)作類實(shí)例,本文用大量代碼和圖片講解Cocos2d-x中的動(dòng)作,代碼中同時(shí)包含大量注釋說明,需要的朋友可以參考下
    2014-09-09
  • C++11利用原子操作實(shí)現(xiàn)自旋鎖

    C++11利用原子操作實(shí)現(xiàn)自旋鎖

    C++自旋鎖是一種低層次的同步原語(yǔ),用于保護(hù)共享資源的訪問,這篇文章主要為大家介紹了如何利用原子操作實(shí)現(xiàn)自旋鎖,感興趣的小伙伴可以了解下
    2023-09-09
  • C/C++之long int與long long的區(qū)別及說明

    C/C++之long int與long long的區(qū)別及說明

    這篇文章主要介紹了C/C++之long int與long long的區(qū)別及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • C++ auto類型說明符

    C++ auto類型說明符

    在C++11中引入了auto類型說明符,用它就能讓編譯器替我們?nèi)シ治霰磉_(dá)式所屬的類型。當(dāng)然,auto變量必須有初始值,這樣編譯器才能推斷其類型
    2016-03-03
  • OpenCV使用BSM統(tǒng)計(jì)視頻中移動(dòng)的對(duì)象

    OpenCV使用BSM統(tǒng)計(jì)視頻中移動(dòng)的對(duì)象

    這篇文章主要為大家詳細(xì)介紹了OpenCV如何使用BackgroundSubstractor(BSM)實(shí)現(xiàn)視頻中移動(dòng)對(duì)象統(tǒng)計(jì)功能,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2023-02-02
  • VC++中HTControl控件類之CHTRichEdit富文本編輯控件實(shí)例

    VC++中HTControl控件類之CHTRichEdit富文本編輯控件實(shí)例

    這篇文章主要介紹了VC++中HTControl控件類之CHTRichEdit富文本編輯控件,是一個(gè)比較實(shí)用的功能,需要的朋友可以參考下
    2014-08-08
  • C++基本用法實(shí)踐之智能指針詳解

    C++基本用法實(shí)踐之智能指針詳解

    為了減少手動(dòng)管理內(nèi)存帶來的困擾,c++提出了智能指針,可以幫助我們進(jìn)行內(nèi)存管理,下面小編就來和大家簡(jiǎn)單聊聊C++中智能指針的基本用法吧
    2023-07-07

最新評(píng)論

拜城县| 祥云县| 黄骅市| 祁阳县| 牡丹江市| 满洲里市| 米林县| 渝北区| 平潭县| 柳河县| 拜城县| 贵溪市| 高碑店市| 长寿区| 闽清县| 积石山| 会宁县| 双峰县| 婺源县| 鸡西市| 上高县| 永昌县| 志丹县| 遂宁市| 新竹市| 阳西县| 手游| 苏尼特左旗| 亚东县| 昌江| 吴旗县| 长宁县| 启东市| 永济市| 清远市| 同仁县| 建水县| 历史| 鹰潭市| 清河县| 罗定市|