C/C++雜談-printf的可變參數機制

C/C++雜談-printf的可變參數機制

文章目錄

  • C/C++雜談-printf的可變參數機制
    • printf的使用
    • printf的源碼
      • 源碼剖析
    • 多參數實現機制原理

C++11引入了可變參數模板機制,對模板參數進行了高度泛化,但是對于可變參數其實C語言學習中早已遇到過,那就是printf可以進行多參數的輸出,這是怎么實現的呢?

printf的使用

我們對于printf的用法無非兩種

    const char *str = "hello , world\n";printf(str);//直接傳入字符串地址int year = 2023;printf("%d%s", year, "原神啟動");//傳入格式控制字符串地址和參數

我們printf的參數是先是一個字符串,后面才是我們的輸出變量,可以嗅出printf對于多參數的控制應該和傳入的第一個字符串有關,那么究竟是如何實現的呢?

printf的源碼

//acenv.h
typedef char *va_list;
#define  _AUPBND        (sizeof (acpi_native_int) - 1)
#define  _ADNBND        (sizeof (acpi_native_int) - 1)#define _bnd(X, bnd)    (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)   (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)      (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
//start.c
static char sprint_buf[1024];
int printf(char *fmt, ...)//格式控制字符串和C的函數多參數
{va_list args;//va_list就是char * 的typedefint n;va_start(args, fmt);n = vsprintf(sprint_buf, fmt, args);va_end(args);write(stdout, sprint_buf, n);return n;
}
//unistd.h
static inline long write(int fd, const char *buf, off_t count)
{return sys_write(fd, buf, count);
}

源碼剖析

映入眼簾的就是一串宏定義

看我們printf的函數參數部分,char *fmt就是我們的格式控制字符串,后面的…是C的函數多參數,即后面的參數數目不定

va_list就是char * 的typedef,也就是定義了名為args的char指針

va_start(args, fmt);就是把args指向fmt后面的第一個參數的地址

這里對va_start進行解釋

#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

可見ap指向的是A地址往后偏移_bnd字節,而 _bnd 傳參了 _ADNBND,_ADNBND = (sizeof (acpi_native_int) - 1)

typedef s32 acpi_native_int 若采用這種宏定義,表明int 類型是用32位表示,也表示當前內存是4字節對齊

acpi_native_int這個參數是平臺相關的

所以sizeof (acpi_native_int)是當前平臺的int大小,我們假設是4字節,那么_ADNBND就是3

#define _bnd(char,3)    ==>  (1+3)&(~3)  ==> 4#define _bnd(int,3)     ==>  (4+3)&(~3)  ==> 4#define _bnd(double,3)  ==>  (4+3)&(~3)  ==> 8

我們通過上述樣例可以明白**_bnd就是獲取類型A的內存對齊大小**,假如32位平臺那么就是4的倍數

所以va_start(args, fmt) 就是把fmt偏移char*內存對齊大小個字節然后賦值給args,這樣args指向的就是格式字符串后面的參數

n = vsprintf(sprint_buf, fmt, args);這里的n則是我們實際控制輸出的字符數,我們printf實際就是一個輸出字符的函數,n也是我們的返回值

而后面的 write(stdout, sprint_buf, n);無非就是把緩沖區里的n個字符輸出到stdout輸出流,這就不是我們討論的重點了

多參數實現機制原理

通過上面的剖析,我們發現printf由格式控制字符串得到下一個參數的起始地址,而下一個起始地址是fmt地址偏移內存對齊大小個字節

這是為什么呢?

這跟函數的壓棧順序有關。我們C/C++默認__cdel的從右至左將參數壓棧,而我們棧是向下增長的,所以先入棧的地址高,后入棧的地址低,所以格式字符串的地址最低,往上偏移自然能得到其他參數的地址

void func(int a, int b, int c)
{printf("a = %d located [%x]\n", a, &a);printf("b = %d located [%x]\n", b, &b);printf("c = %d located [%x]\n", c, &c);
}
signed main()
{func(1, 2, 3);return 0;
}
//輸出
a = 1 located [b3bff960]
b = 2 located [b3bff968]
c = 3 located [b3bff970]

得到地址后,由于我們規定格式控制字符串中%的數量即為輸出參數數量,然后就能拿到所有參數放到緩沖區,再輸出到標準輸出流

如果我們想要實現多參數機制(需要了解<stdarg.h>),自然也要通過我們的參數設定模式,類似格式控制中百分號的數量來確定參數的數目,而名稱出現的順序對應參數的順序。

可見C語言的多參數機制是很繁瑣的,而我們C++11引入可變參數模板也正是為了追求更好的參數泛化。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160948.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160948.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160948.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Redis】持久化-RDBAOF混合持久化

文章目錄 前置知識RDB&#xff08;定期備份&#xff09;觸發機制流程說明RDB文件的處理RDB 的優缺點 AOF&#xff08;實時備份&#xff09;使用AOF命令寫入AOF工作流程文件同步重寫機制重寫觸發機制AOF進制重寫流程 混合持久化啟動時數據恢復 總結 前置知識 回顧MySQL MySQL的事…

LeetCode(28)盛最多水的容器【雙指針】【中等】

目錄 1.題目2.答案3.提交結果截圖 鏈接&#xff1a; 盛最多水的容器 1.題目 給定一個長度為 n 的整數數組 height 。有 n 條垂線&#xff0c;第 i 條線的兩個端點是 (i, 0) 和 (i, height[i]) 。 找出其中的兩條線&#xff0c;使得它們與 x 軸共同構成的容器可以容納最多的水…

云計算時代改變了什么?

當今云計算時代&#xff0c;計算資源和數據存儲已經不再受限于本地設備的硬件和軟件限制。云計算技術的發展和普及&#xff0c;使得用戶可以通過互聯網訪問大量的計算資源和存儲空間&#xff0c;從而改變了我們對計算和數據的看法。本文將探討云計算時代對計算和數據處理的影響…

對線程的創建

一&#xff0c;概括 二&#xff0c;線程構建方式一&#xff08;繼承Thread類&#xff09; 三&#xff0c;案例 父類&#xff1a; package Duoxiancheng;public abstract class Name {public static void main(String[] args) {//3&#xff0c;創建一個Thread線程類對象Thr…

匯編語言學習筆記

匯編語言的不同種類 as86匯編&#xff1a;能產生16位代碼的Intel 8086匯編 mov ax, cs //cs→ax&#xff0c;目標操作數在前GNU as匯編&#xff1a;產生32位代碼&#xff0c;使用AT&T系統V語法 movl var&#xff0c; %eax // var→%eax&#xff0c;目標操作數在后內嵌匯編…

Android異步之旅:探索AsyncTask

前言&#xff1a; 在Android應用程序開發中&#xff0c;異步操作是非常常見的需求。比如&#xff0c;我們可能需要在后臺線程中執行網絡請求、數據庫操作或者其他耗時的任務&#xff0c;而不阻塞UI線程。為了實現這些異步操作&#xff0c;Android提供了多種方式&#xff0c;其…

基于Qt的UDP通信、TCP文件傳輸程序的設計與實現——QQ聊天群聊

&#x1f64c;秋名山碼民的主頁 &#x1f602;oi退役選手&#xff0c;Java、大數據、單片機、IoT均有所涉獵&#xff0c;熱愛技術&#xff0c;技術無罪 &#x1f389;歡迎關注&#x1f50e;點贊&#x1f44d;收藏??留言&#x1f4dd; 獲取源碼&#xff0c;添加WX 目錄 前言一…

PostgreSQL序列,怎么才能第二天重新從1開始計數

--確定日期最大值和每天序列號最大值 with cte as(select (((1::bigint)<<32)-1) as max_date_second,(((1::bigint)<<31)-1) as max_sn )select max_date_second,to_timestamp(max_date_second),max_sn,((max_date_second<<31)|max_sn) as max_val,(((max_d…

Selenium 元素不能定位總結

目錄 元素不能定位總結: 1、定位語法錯誤&#xff1a; 定位語法錯誤&#xff0c;如無效的xpath&#xff0c;css selector,dom路徑錯誤&#xff0c;動態dom 定位語法錯誤&#xff0c;動態路徑&#xff08;動態變化&#xff09; 定位策略錯誤&#xff0c;如dom沒有id用id定位…

研發探索:導購APP、查券返利機器人與淘客系統,全面對比與選擇

研發探究&#xff1a;導購APP、查券返利機器人與淘客系統&#xff0c;全面對比與選擇 在互聯網購物的時代&#xff0c;導購APP、淘客機器人和微賺淘客系統成為了消費者們的三大好幫手。它們各具優勢&#xff0c;但也存在一些不足。本文將為您詳細對比這三種工具&#xff0c;幫…

vue history路徑編碼

記錄今天遇到的一個問題&#xff1a; 問題現狀 有一個需要前端偽造302進行重定向的需求&#xff0c;我們需要將這樣的一個路徑&#xff1a;http://xxx.com/system-name/#/index&#xff0c;拼接在跳轉地址的后面&#xff0c;進行重定向。拼接的方式是這樣的&#xff1a; htt…

攻防世界-web-Confusion1

1. 題目描述 打開鏈接&#xff0c;如圖 點擊Login和Rigister&#xff0c;都報錯 但是有提示 指出了flag所在的位置&#xff0c;題目中直接能獲取到的信息暫時就這么些了 2. 思路分析 既然告訴了我們flag文件的位置&#xff0c;那么要讀取到這個文件&#xff0c;要么是任意文…

AI輔助帶貨直播場景源碼系統 附帶網站的搭建教程

互聯網技術的發展和普及&#xff0c;直播帶貨行業迅速崛起。然而&#xff0c;直播帶貨在帶來商機的同時&#xff0c;也面臨著諸多挑戰。如直播內容缺乏新意、轉化率低等問題。針對這些問題&#xff0c;AI輔助帶貨直播場景源碼系統應運而生&#xff0c;旨在利用人工智能技術&…

【高級滲透篇】網絡安全面試

【高級滲透篇】網絡安全面試 1.權限維持2.代碼安全Python語法相關 1.權限維持 Linux權限維持方法論 Windows權限維持方法論 2.代碼安全 Python 語法相關 1、Python的值類型和引用類型是哪些 Python 中的值類型包括&#xff1a; 數字類型&#xff08;如整數、浮點數、復數…

對接蘋果支付退款退單接口

前言 一般而言&#xff0c;我們其實很少對接退款接口&#xff0c;因為退款基本都是商家自己決定后進行操作的&#xff0c;但是蘋果比較特殊&#xff0c;用戶可以直接向蘋果發起退款請求&#xff0c;蘋果覺得合理會退給用戶&#xff0c;但是目前公司業務還是需要對接這個接口&am…

試試MyBatis-Plus可視化代碼生成器,太香了,你一定會感謝我

前言 在基于Mybatis的開發模式中&#xff0c;很多開發者還會選擇Mybatis-Plus來輔助功能開發&#xff0c;以此提高開發的效率。雖然Mybatis也有代碼生成的工具&#xff0c;但Mybatis-Plus由于在Mybatis基礎上做了一些調整&#xff0c;因此&#xff0c;常規的生成工具生成的代碼…

PC端使子組件的彈框關閉

子組件 <template><el-dialog title"新增部門" :visible"showDialog" close"close"> </el-dialog> </template> <script> export default {props: {showDialog: {type: Boolean,default: false,},},data() {retu…

【JavaSE】-5-嵌套循環

回顧 一、java語言特點 二、配置java環境 path 三、記事本 javac -d . java 包名.類名 四、eclipse 五、變量 定義變量 數據類型 變量名值; 六、相關的數據類型 ? 基本&#xff08;四類 、8種&#xff09;、引用 ? 類型轉換&#xff08;自動、強制&#xff09; ? 運…

Java面向對象(高級)-- 類中屬性賦值的位置及過程

文章目錄 一、賦值順序&#xff08;1&#xff09;賦值的位置及順序&#xff08;2&#xff09;舉例&#xff08;3&#xff09;字節碼文件&#xff08;4&#xff09;進一步探索&#xff08;5&#xff09;最終賦值順序&#xff08;6&#xff09;實際開發如何選 二、(超綱)關于字節…

1992-2021年省市縣經過矯正的夜間燈光數據(GNLD、VIIRS)

1992-2021年省市縣經過矯正的夜間燈光數據&#xff08;GNLD、VIIRS&#xff09; 1、時間&#xff1a;1992-2021年3月&#xff0c;其中1992-2013年為年度數據&#xff0c;2013-2021年3月為月度數據 2、來源&#xff1a;DMSP、VIIRS 3、范圍&#xff1a;分區域匯總&#xff1a…