sprintf用法詳解

printf可能是許多程序員在開始學習C語言時接觸到的第二個函數(我猜第一個是main),說起來,自然是老朋友了,可是,你對這個老朋友了解多嗎?你對它的那個孿生兄弟sprintf了解多嗎?在將各種類型的數據構造成字符串時,sprintf的強大功能很少會讓你失望。

由于sprintfprintf在用法上幾乎一樣,只是打印的目的地不同而已,前者打印到字符串中,后者則直接在命令行上輸出。這也導致sprintfprintf有用得多。所以本文著重介紹sprintf,有時也穿插著用用pritnf

sprintf是個變參函數,定義如下:

int sprintf( char *buffer, const char *format [,argument] ... );

除了前兩個參數類型固定外,后面可以接任意多個參數。而它的精華,顯然就在第二個參數:格式化字符串上。

printfsprintf都使用格式化字符串來指定串的格式,在格式串內部使用一些以“%”開頭的格式說明符(format specifications)來占據一個位置,在后邊的變參列表中提供相應的變量,最終函數就會用相應位置的變量來替代那個說明符,產生一個調用者想要的字符串。

1.??????格式化數字字符串

sprintf最常見的應用之一莫過于把整數打印到字符串中,所以,spritnf在大多數場合可以替代itoa。如:

//把整數123打印成一個字符串保存在s中。

sprintf(s,"%d", 123);?? //產生"123"

可以指定寬度,不足的左邊補空格:

sprintf(s,"%8d%8d", 123, 4567); //產生:"????123???4567"

當然也可以左對齊:

sprintf(s,"%-8d%8d", 123, 4567); //產生:"123????????4567"

也可以按照16進制打印:

sprintf(s,"%8x", 4567); //小寫16進制,寬度占8個位置,右對齊

sprintf(s,"%-8X", 4568); //大寫16進制,寬度占8個位置,左對齊

這樣,一個整數的16進制字符串就很容易得到,但我們在打印16進制內容時,通常想要一種左邊補0的等寬格式,那該怎么做呢?很簡單,在表示寬度的數字前面加個0就可以了。

sprintf(s,"%08X", 4567); //產生:"000011D7"

上面以”%d”進行的10進制打印同樣也可以使用這種左邊補0的方式。

這里要注意一個符號擴展的問題:比如,假如我們想打印短整數(short-1的內存16進制表示形式,在Win32平臺上,一個short型占2個字節,所以我們自然希望用416進制數字來打印它:

short si = -1;

sprintf(s,"%04X", si);

產生“FFFFFFFF”,怎么回事?因為spritnf是個變參函數,除了前面兩個參數之外,后面的參數都不是類型安全的,函數更沒有辦法僅僅通過一個“%X”就能得知當初函數調用前參數壓棧時被壓進來的到底是個4字節的整數還是個2字節的短整數,所以采取了統一4字節的處理方式,導致參數壓棧時做了符號擴展,擴展成了32位的整數-1,打印時4個位置不夠了,就把32位整數-1816進制都打印出來了。如果你想看si的本來面目,那么就應該讓編譯器做0擴展而不是符號擴展(擴展時二進制左邊補0而不是補符號位):

sprintf(s,"%04X", (unsigned short)si);

就可以了。或者:

unsigned shortsi = -1;

sprintf(s,"%04X", si);

sprintfprintf還可以按8進制打印整數字符串,使用”%o”。注意8進制和16進制都不會打印出負數,都是無符號的,實際上也就是變量的內部編碼的直接的16進制或8進制表示。

2.??????控制浮點數打印格式

浮點數的打印和格式控制是sprintf的又一大常用功能,浮點數使用格式符”%f”控制,默認保留小數點后6位數字,比如:

sprintf(s,"%f", 3.1415926);??? //產生"3.141593"

但有時我們希望自己控制打印的寬度和小數位數,這時就應該使用:”%m.nf”格式,其中m表示打印的寬度,n表示小數點后的位數。比如:

sprintf(s,"%10.3f",3.1415626);?? //產生:"???? 3.142"

sprintf(s,"%-10.3f",3.1415626);?//產生:"3.142????"

sprintf(s,"%.3f", 3.1415626);//不指定總寬度,產生:"3.142"

注意一個問題,你猜

int i = 100;

sprintf(s,"%.2f", i);

會打出什么東東來?“100.00”?對嗎?自己試試就知道了,同時也試試下面這個:

sprintf(s,"%.2f", (double)i);

第一個打出來的肯定不是正確結果,原因跟前面提到的一樣,參數壓棧時調用者并不知道跟i相對應的格式控制符是個”%f”。而函數執行時函數本身則并不知道當年被壓入棧里的是個整數,于是可憐的保存整數i的那4個字節就被不由分說地強行作為浮點數格式來解釋了,整個亂套了。

不過,如果有人有興趣使用手工編碼一個浮點數,那么倒可以使用這種方法來檢驗一下你手工編排的結果是否正確。

字符/Ascii碼對照

我們知道,在C/C++語言中,char也是一種普通的scalable類型,除了字長之外,它與shortintlong這些類型沒有本質區別,只不過被大家習慣用來表示字符和字符串而已。(或許當年該把這個類型叫做“byte”,然后現在就可以根據實際情況,使用byteshort來把char通過typedef定義出來,這樣更合適些)

于是,使用”%d”或者”%x”打印一個字符,便能得出它的10進制或16進制的ASCII碼;反過來,使用”%c”打印一個整數,便可以看到它所對應的ASCII字符。以下程序段把所有可見字符的ASCII碼對照表打印到屏幕上(這里采用printf,注意”#””%X”合用時自動為16進制數增加”0X”前綴):

for(int i = 32; i <127; i++) {

???printf("[ %c ]: %3d 0x%#04X\n", i, i, i);

}

3.??????連接字符串

sprintf的格式控制串中既然可以插入各種東西,并最終把它們連成一串,自然也就能夠連接字符串,從而在許多場合可以替代strcat,但sprintf能夠一次連接多個字符串(自然也可以同時在它們中間插入別的內容,總之非常靈活)。比如:

char* who ="I";

char* whom ="CSDN";

sprintf(s,"%s love %s.", who, whom); //產生:"I love CSDN. "

strcat只能連接字符串(一段以’\0’結尾的字符數組或叫做字符緩沖,null-terminated-string),但有時我們有兩段字符緩沖區,他們并不是以’\0’結尾。比如許多從第三方庫函數中返回的字符數組,從硬件或者網絡傳輸中讀進來的字符流,它們未必每一段字符序列后面都有個相應的’\0’來結尾。如果直接連接,不管是sprintf還是strcat肯定會導致非法內存操作,而strncat也至少要求第一個參數是個null-terminated-string,那該怎么辦呢?我們自然會想起前面介紹打印整數和浮點數時可以指定寬度,字符串也一樣的。比如:

char a1[] ={'A', 'B', 'C', 'D', 'E', 'F', 'G'};

char a2[] ={'H', 'I', 'J', 'K', 'L', 'M', 'N'};

如果:

sprintf(s,"%s%s", a1, a2); //Don't do that!

十有八九要出問題了。是否可以改成:

sprintf(s,"%7s%7s", a1, a2);

也沒好到哪兒去,正確的應該是:

sprintf(s,"%.7s%.7s", a1, a2);//產生:"ABCDEFGHIJKLMN"

這可以類比打印浮點數的”%m.nf”,在”%m.ns”中,m表示占用寬度(字符串長度不足時補空格,超出了則按照實際寬度打印),n才表示從相應的字符串中最多取用的字符數。通常在打印字符串時m沒什么大用,還是點號后面的n用的多。自然,也可以前后都只取部分字符:

sprintf(s,"%.6s%.5s", a1, a2);//產生:"ABCDEFHIJKL"

在許多時候,我們或許還希望這些格式控制符中用以指定長度信息的數字是動態的,而不是靜態指定的,因為許多時候,程序要到運行時才會清楚到底需要取字符數組中的幾個字符,這種動態的寬度/精度設置功能在sprintf的實現中也被考慮到了,sprintf采用”*”來占用一個本來需要一個指定寬度或精度的常數數字的位置,同樣,而實際的寬度或精度就可以和其它被打印的變量一樣被提供出來,于是,上面的例子可以變成:

sprintf(s,"%.*s%.*s", 7, a1, 7, a2);

或者:

sprintf(s,"%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2);

實際上,前面介紹的打印字符、整數、浮點數等都可以動態指定那些常量值,比如:

sprintf(s,"%-*d", 4, 'A'); //產生"65?"

sprintf(s,"%#0*X", 8, 128);??? //產生"0X000080""#"產生0X

sprintf(s,"%*.*f", 10, 2, 3.1415926); //產生"????? 3.14"

4.??????打印地址信息

有時調試程序時,我們可能想查看某些變量或者成員的地址,由于地址或者指針也不過是個32位的數,你完全可以使用打印無符號整數的”%u”把他們打印出來:

sprintf(s,"%u", &i);

不過通常人們還是喜歡使用16進制而不是10進制來顯示一個地址:

sprintf(s,"%08X", &i);

然而,這些都是間接的方法,對于地址打印,sprintf?提供了專門的”%p”

sprintf(s,"%p", &i);

我覺得它實際上就相當于:

sprintf(s,"%0*x", 2 * sizeof(void *), &i);

5.??????利用sprintf的返回值

較少有人注意printf/sprintf函數的返回值,但有時它卻是有用的,spritnf返回了本次函數調用最終打印到字符緩沖區中的字符數目。也就是說每當一次sprinf調用結束以后,你無須再調用一次strlen便已經知道了結果字符串的長度。如:

int len =sprintf(s, "%d", i);

對于正整數來說,len便等于整數i10進制位數。

下面的是個完整的例子,產生10[0, 100)之間的隨機數,并將他們打印到一個字符數組s中,以逗號分隔開。

#include<stdio.h>

#include<time.h>

#include<stdlib.h>

int main() {

???srand(time(0));

???char s[64];

???int offset = 0;

???for(int i = 0; i < 10; i++) {

??????offset += sprintf(s + offset, "%d,", rand() % 100);

???}

???s[offset - 1] = '\n';//將最后一個逗號換成換行符。

???printf(s);

???return 0;

}

設想當你從數據庫中取出一條記錄,然后希望把他們的各個字段按照某種規則連接成一個字符串時,就可以使用這種方法,從理論上講,他應該比不斷的strcat效率高,因為strcat每次調用都需要先找到最后的那個’\0’的位置,而在上面給出的例子中,我們每次都利用sprintf返回值把這個位置直接記下來了。

6.??????使用sprintf的常見問題

sprintf是個變參函數,使用時經常出問題,而且只要出問題通常就是能導致程序崩潰的內存訪問錯誤,但好在由sprintf誤用導致的問題雖然嚴重,卻很容易找出,無非就是那么幾種情況,通常用眼睛再把出錯的代碼多看幾眼就看出來了。

??????????緩沖區溢出

第一個參數的長度太短了,沒的說,給個大點的地方吧。當然也可能是后面的參數的問題,建議變參對應一定要細心,而打印字符串時,盡量使用”%.ns”的形式指定最大字符數。

??????????忘記了第一個參數

低級得不能再低級問題,用printf用得太慣了。//偶就常犯。:。(

??????????變參對應出問題

通常是忘記了提供對應某個格式符的變參,導致以后的參數統統錯位,檢查檢查吧。尤其是對應”*”的那些參數,都提供了嗎?不要把一個整數對應一個”%s”,編譯器會覺得你欺她太甚了(編譯器是objexe的媽媽,應該是個女的,:P)。

7.??????strftime

sprintf還有個不錯的表妹:strftime,專門用于格式化時間字符串的,用法跟她表哥很像,也是一大堆格式控制符,只是畢竟小姑娘家心細,她還要調用者指定緩沖區的最大長度,可能是為了在出現問題時可以推卸責任吧。這里舉個例子:

time_t t =time(0);

//產生"YYYY-MM-DD hh:mm:ss"格式的字符串。

char s[32];

strftime(s,sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t));

sprintfMFC中也能找到他的知音:CString::FormatstrftimeMFC中自然也有她的同道:CTime::Format,這一對由于從面向對象哪里得到了贊助,用以寫出的代碼更覺優雅。

8.??????后記

?參量表是需要輸出的一系列參數,?其個數必須與格式化字符串所說明的輸出?
參數個數一樣多,?各參數之間用","分開,?且順序一一對應,??否則將會出現意想?
不到的錯誤。?
????1.?
格式化規定符?
????Turbo C2.0
提供的格式化規定符如下:?
━━━━━━━━━━━━━━━━━━━━━━━━━━?
???
符號??????????????????作用?
──────────────────────────?
????%d??????????????
十進制有符號整數?
????%u??????????????
十進制無符號整數?
????%f??????????????
浮點數?
????%s??????????????
字符串?
????%c??????????????
單個字符?
????%p??????????????
指針的值?
????%e??????????????
指數形式的浮點數?
????%x,%X??????????
無符號以十六進制表示的整數?
????%0??????????????
無符號以八進制表示的整數?
????%g??????????????
自動選擇合適的表示法?
━━━━━━━━━━━━━━━━━━━━━━━━━━?
????
說明:?
????(1).?
可以在"%"和字母之間插進數字表示最大場寬。?
?????
例如:??%3d???表示輸出3位整型數,?不夠3位右對齊。?
????????????%9.2f?
表示輸出場寬為9的浮點數,?其中小數位為2,?整數位為6,?
??????????????????
小數點占一位,?不夠9位右對齊。?
????????????%8s???
表示輸出8個字符的字符串,?不夠8個字符右對齊。?
????
如果字符串的長度、或整型數位數超過說明的場寬,?將按其實際長度輸出。?
但對浮點數,?若整數部分位數超過了說明的整數位寬度,?將按實際整數位輸出;?
若小數部分位數超過了說明的小數位寬度,?則按說明的寬度以四舍五入輸出。?
????
另外,?若想在輸出值前加一些0,?就應在場寬項前加個0?
????
例如:???%04d??表示在輸出一個小于4位的數值時,?將在前面補0使其總寬度?
4位。?
????
如果用浮點數表示字符或整型量的輸出格式,?小數點后的數字代表最大寬度,?
小數點前的數字代表最小寬度。?
????
例如: %6.9s?表示顯示一個長度不小于6且不大于9的字符串。若大于9,???
9個字符以后的內容將被刪除。?
????(2).?
可以在"%"和字母之間加小寫字母l,?表示輸出的是長型數。?
????
例如:???%ld???表示輸出long整數?
????????????%lf???
表示輸出double浮點數?
????(3).?
可以控制輸出左對齊或右對齊,?即在"%"和字母之間加入一個"-"?號可?
說明輸出為左對齊,?否則為右對齊。?
????
例如:???%-7d??表示輸出7位整數左對齊?
????????????%-10s?
表示輸出10個字符左對齊?
????2.?
一些特殊規定字符?
━━━━━━━━━━━━━━━━━━━━━━━━━━?
????
字符???????????????????????????作用?
──────────────────────────?
?????\n???????????????????
換行?
?????\f???????????????????
清屏并換頁?
?????\r???????????????????
回車?
?????\t???????????????????Tab
?
?????\xhh?????????????????
表示一個ASCII碼用16進表示,?
??????????????????????????
其中hh1216進制數?
━━━━━━━━━━━━━━━━━━━━━━━━━

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

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

相關文章

掌握 Ajax,第 2 部分: 使用 JavaScript 和 Ajax 發出異步請求

轉http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro2/ 掌握 Ajax&#xff0c;第 2 部分: 使用 JavaScript 和 Ajax 發出異步請求 在 Web 請求中使用 XMLHttpRequest 多數 Web 應用程序都使用請求/響應模型從服務器上獲得完整的 HTML 頁面。常常是點擊一個按鈕&#xff0…

Provisioning Services 7.8 入門系列教程之十一 通過版本控制自動更新虛擬磁盤

續Provisioning Services 7.8 入門系列教程之十 通過類自動更新虛擬磁盤從前兩的兩種更新方式可以看出&#xff0c;它們有一個共同的特點&#xff0c;即需要產生&#xff08;復制&#xff09;完成的虛擬磁盤副本&#xff0c;然后進行相關的升級操作。這兩種方法在實際生產中&am…

OC面試題

什么是KVC和KVO&#xff1f; 答&#xff1a;KVC(Key-Value-Coding)內部的實現&#xff1a;一個對象在調用setValue的時候&#xff0c; &#xff08;1&#xff09;首先根據方法名找到運行方法的時候所需要的環境參數。 &#xff08;2&#xff09;他會從自己isa指針結合環境參數&…

【算法】QuickSort

快速排序&#xff0c;時間復雜度O(N*logN)&#xff0c;要能熟練掌握&#xff01; 以下主要參考http://blog.csdn.net/morewindows/article/details/6684558&#xff0c; 感謝原博主&#xff01; 該方法的基本思想是&#xff1a; 1&#xff0e;先從數列中取出一個數作為基準數。…

串口之GetCommState、SetCommState函數詳解

GetCommState 讀取串口設置(波特率,校驗,停止位,數據位等).函數聲明&#xff1a;BOOL GetCommState(HANDLE hFile,LPDCB lpDCB);GetCommState函數的第一個參數hFile是由CreateFile函數返回指向已打開串行口的句柄。第二個參數指向設備控制塊DCB。如果函數調用成功&#xff0c;則…

登錄失敗時記住訪問的地址

登錄失敗時記住訪問的地址 使用spring MVC 訪問時,在攔截器中記錄訪問的地址: Java代碼 String path request.getRequestURI();//"/demo_channel_terminal/news/list" System.out.println("您無權訪問:" path); //用于登錄成功…

串口之GetCommTimeouts、SetCommTimeouts函數詳解

Windows系統利用此函數獲取特定的通訊設備讀寫時的超時參數設定&#xff0c;GetCommTimeouts函數聲明如下&#xff1a;BOOL GetCommTimeouts(HANDLE hFile,LPCOMMTIMEOUTS lpCommTimeouts);GetCommTimeouts函數的第一個參數hFile是由CreateFile函數返回指向已打開串行口的句柄。…

GUN/LINUX命令之 cp mv install

1. cp命令 復制copy命令的簡寫 SYNOPSIS cp [OPTION]... [-T] SOURCE DEST cp [OPTION]... SOURCE... DIRECTORY cp [OPTION]... -t DIRECTORY SOURCE... cp SOURCE DEST 后者如果是目錄那么源文件就復制到文件夾里面并且保持著原來的名字&#xff1b;如果D…

Tomcat - Maven plugin: 運行找不到webapp

2019獨角獸企業重金招聘Python工程師標準>>> The tomcat7-maven-plugin allows running the current project as a Web application and additional <webapps> can be specified that will be simultaneously loaded into tomcat. My project is not a Web ap…

面試題3

1. 你如何理解 iOS 內存管理 1. new alloc copy retain這些對象我們都要主動的release或者 autorelease 2. 如果是類方法創建的對象,那么系統自動釋放池自動在適當的 時候會幫我們 release 3. ARC xcode 自動會幫我們人工智能的添加 release autorelease 操 作 2. C語言里的數…

基于MQTT協議進行應用開發

來自&#xff1a;http://www.cnblogs.com/secondtononewe/p/6073089.html 官方協議有句如下的話來形容MQTT的設計思想&#xff1a; “It is designed for connections with remote locations where a "small code footprint" is required or the network bandwidth i…

SortedDictionaryTKey,TValue正序與反序排序及Dicttionary相關

SortedDictionary<TKey,TValue>能對字典排序 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace SortDictionary {class Program{static void Main(string[] args){TestDictionarySort();…

DOS窗口的編碼頁從UTF-8調回GBK

2019獨角獸企業重金招聘Python工程師標準>>> 之前在DOS窗口操作MySQL數據庫的時候&#xff0c;將編碼頁從GBK設置成了UTF-8&#xff0c;解決了在DOS窗口顯示MySQL數據庫中的表中的中文字符出現亂碼的問題。但是除此之外&#xff0c;DOS窗口顯示的其他中文字符都是亂…

UIBezierPath

學習UIBezierPath畫圖 筆者在寫本篇文章之前&#xff0c;也沒有系統學習過貝塞爾曲線&#xff0c;只是曾經某一次的需求需要使用到&#xff0c;才臨時百度看了一看而且使用最基本的功能。現在總算有時間停下來好好研究研究這個神奇而偉大的貝塞爾先生&#xff01; 筆者在學習時…

系統架構設計理論與原則

一、無共享架構 1、無共享架構 無共享架構是一種分布式計算架構&#xff0c;這種架構中不存在集中存儲的狀態&#xff0c;系統中每個節點都是獨立自治的&#xff0c;整個系統中沒有資源競爭&#xff0c;這種架構具有非常強的擴張性&#xff0c;目前在web應用中被廣泛使用。 無共…

VS2010 教程:創建一個 WPF 應用程序 (第一節)

來自&#xff1a;https://msdn.microsoft.com/zh-cn/library/ff629048.aspx [原文發表地址] VS2010 Tutorial: Build a WPF App (Step 1) [原文發表時間] Friday, May 22, 2009 8:00 AM 這篇文章里&#xff0c;我將使用VS2010 Beta 1創建一個WPF 應用程序。并且 我將展示這個產…

js 日期星期 帶農歷

Weekday代碼 //得到當前日期如2009年6月19日 星期五 function getDate(){ var today new Date(); var x new Array("星期日", "星期一", "星期二","星期三","星期四", "星期五","星期六"…

FMDB的使用

// // FMDBmanager.h // database // // Created by PRL on 16/10/13. // Copyright © 2016年PRL. All rights reserved. // #import <Foundation/Foundation.h> interface FMDBmanager : NSObject{ FMDatabase * _db; } (FMDBmanager *)sharedManager; //獲取…

深入淺出WPF之Binding的使用(一)

from: http://www.cnblogs.com/akwwl/p/3421005.html 在WPF中Binding可以比作數據的橋梁&#xff0c;橋梁的兩端分別是Binding的源&#xff08;Source&#xff09;和目標&#xff08;Target&#xff09;。 一般情況下&#xff0c;Binding源是邏輯層對象&#xff0c;Binding目…

arm處理器中a5 a8 a9,v6 v7,arm7 arm9 arm11都是依據什么來分類的【轉】

轉自&#xff1a;http://blog.csdn.net/maochengtao/article/details/9951131ARM處理器發展這么多年&#xff0c;有很多架構&#xff0c;很多不同的內核 架構有armv1 v2 v3 v4 v5 v6 v7 內核太多了&#xff0c;比如armv1對應的是arm1&#xff0c;armv5對應的arm9&#xff0c;ar…