有關printf的小問題

2021-06-20 00:08:11 字數 2942 閱讀 8559

列印結構體

曾對c++的迭代器不甚了解,於是嘗試用printf直接列印迭代器,得到了詭異的輸出結果。

測試系統:win7+vs2010

// cpp

#include #include #include using namespace std;

int main()

// output:

// *i=1, i=4539464, *i=0, i=4539672, *i=1, i=4539464, *i=0

當時就震驚了,一時無法理解為何有這樣的輸出結果。再進行嘗試,發現c中也有類似的問題。

// c

#include struct s;

int main()

// output:

// 0, 1, 2

// 0, 1, 2, 0, 0, 2147299328

第乙個printf給人一種錯覺就好像s變換了它的列印值!不過自己好好想想,發現不難理解:printf中指示列印物件的指標每次只移動int的長度,因此列印三次%d只是列印了第乙個s的三個成員;後兩個s雖然作為引數傳了進來,但是根本沒有被printf列印。至於第二個printf,只是先輸出了s的所有成員,然後輸出了記憶體中排在s後面的一些無意義的資料罷了。

這裡我有乙個地方不明白,為何每次都是0, 0, 2147299328?即十六進製制的00000000 00000000 7ffd3000?這些資料有何意義?

反觀迭代器的問題也可以用同樣的方法解釋了:迭代器i是乙個class,在記憶體中佔據三個int的長度,對應int值分別為4539464, 0, 4539672。

但我不明白的是,這4539464, 0, 4539672有何含義?為什麼第三個數總是比第二個數大208?

// c

int main()

// output:

// 0.000000 0

上面這段**也是乙個問題,讀取%f會讀取8個位元組,將輸入引數a和b都讀完了,而整數值1和2組成的8個位元組翻譯為double是0;第二個0僅是記憶體中無意義的資料。

入棧順序

// c

#include int main()

// output:

// 4, 3, 2, 4, 4, 4

對結果進行分析,發現輸入引數是

從右到左結算的,經過四次自增運算i變為4。到左數第二個i++時進行了兩次自增,返回值為2;到左數第乙個i++時進行了三次自增,返回值為3;而++i則是以i作為返回值的,因此最後都會返回i的值。

入棧順序的問題讓我想起了之前研究過的呼叫約定(calling convention)。呼叫約定決定以下內容:

* 引數和返回值放置的位置(在暫存器中; 在呼叫棧中; 兩者混合)

* 引數傳遞的順序(或者單個引數不同部分的順序)

* 呼叫前設定和呼叫後清理的工作, 在呼叫者和被呼叫者之間如何分配

* 被呼叫者可以直接使用哪乙個暫存器有時也包括在內. (否則的話被當成abi的細節)

* 哪乙個暫存器被當作volatile的或者非volatile的, 並且如果是volatile的, 不需要被呼叫者恢復

1. _cdecl (c呼叫約定, the c default calling convention, c/c++預設呼叫方式)

按從右至左的順序壓引數入棧, 由呼叫者把引數彈出棧. 對於"c"函式或者變數, 修飾名是在函式名前加下劃線. 對於"c++"函式, 有所不同.     

如函式void test(void)的修飾名是_test; 對於不屬於乙個類的"c++"全域性函式, 修飾名是?test@@zaxxz.

這是mfc預設呼叫約定. 由於是呼叫者負責把引數彈出棧, 所以可以給函式定義個數不定的引數, 如printf函式.

2. _stdcall (pascal方式清理c方式壓棧, 通常用於win32 api中)

按從右至左的順序壓引數入棧, 由被呼叫者把引數彈出棧. 對於"c"函式或者變數, 修飾名以下劃線為字首, 然後是函式名, 然後是符號"@"及引數的位元組數, 如函式int func(int a, double b)的修飾名是_func@12. 對於"c++"函式, 則有所不同.

所有的win32 api函式都遵循該約定.

3. _fastcall (快速呼叫約定, 通過暫存器來傳送引數)

頭兩個dword型別或者佔更少位元組的引數被放入ecx和edx暫存器, 其他剩下的引數按從右到左的順序壓入棧. 由被呼叫者把引數彈出棧, 對於"c"函式或者變數, 修飾名以"@"為字首, 然後是函式名, 接著是符號"@"及引數的位元組數, 如函式int func(int a, double b)的修飾名是@func@12. 對於"c++"函式, 有所不同.

未來的編譯器可能使用不同的暫存器來存放引數.

4. thiscall (本身呼叫, 僅用於"c++"成員函式)

5. naked call (裸調)

採用1-4的呼叫約定時, 如果必要的話, 進入函式時編譯器會產生**來儲存esi, edi, ebx, ebp暫存器, 退出函式時則產生**恢復這些暫存器的內容. naked call不產生這樣的**.

naked call不是型別修飾符, 故必須和_declspec共同使用, 如下:

__declspec(naked) int func(formal_parameters)

有關指令碼的小問題

1.寫乙個ping命令的bat檔案 我想寫乙個ping命令的bat檔案,這樣寫不行,請高手指點一下 先用記事本寫ping www.163.com,然後將其儲存為ping.bat 這樣執行出來異常,請指點!解決辦法 不要將其命名為ping.bat 換個名稱 echo off title 網路連線狀況檢...

兩個與位運算有關的小問題

兩個與位運算有關的小問題 在讀 程式設計之美 一書時,書中提到兩個小問題 1.如何求算n 的二進位制表示最低位1的位置。2.如何用最簡便最快的方法判斷乙個正整數是否是2的方冪。對於第乙個問題 對於任何乙個整數n,當表示成二進位制時,若最低位為1,則該數肯定是奇數,否則為偶數。若是奇數,則n肯定不含質...

乙個有關原碼 反碼 補碼的小問題

int x 1 y x 求y的值。我開始算出結果為e,不過這個結果是錯的。思路 因為計算機是以二進位制的補碼的形式來儲存,所以我們要將 1 轉換成補碼,又因為補碼是在反碼的基礎上轉換而來,而反碼又是原碼轉換來的,所以得將 1 裝換成原碼。以下是正確演算法 1 原 00000001 1 原 11111...