深入理解c格式化輸出

格式化輸出考察了不僅考察各種數據類型的轉換以及你對內存的理解

首先,要很清楚各個不同數據類型的大小,這個做面向對象的基礎

#include <stdio.h>

printf("size of int:\t\t[%d]\n", sizeof(int));
printf("size of long:\t\t[%d]\n", sizeof(long));
printf("size of long long:\t[%d]\n", sizeof(long long));
printf("size of float:\t\t[%d]\n", sizeof(float));
printf("size of double:\t\t[%d]\n", sizeof(double));

//size of int:            [4]
//size of long:           [8]
//size of long long:      [8]
//size of float:          [4]
//size of double:         [8]

 

首先考慮兩類

一類是實際值,例如int/long/short,另一類是字面值,例如ascii

int的數據大小是4個bytes,最大可以表示7f ff ff ff ,最小80 00 00 00

int a = 0x7FFFFFFF;
printf("MAX %04x\n",a); //7fffffff
printf("MAX %d\n",a); //2147483647


a = 0x80000000;
printf("MIN %04x\n",a); //80000000
printf("MIN %d\n",a); //-2147483648

而一個char只佔1個byte。

這裏說一下格式化輸出的符號

%d 以10進制方式輸出

%c 以ascii方式輸出

%x 以16進制方式(a/b/c/d/e/f 小寫)輸出, %X 以16進制方式(A/B/C/D/E/F 大寫)輸出

%04x 以16進制輸出,指定輸出長度4位,左邊不夠用0補而不是空白符

printf輸出時,爲了便於閱讀,會進一步轉換,[1]以ascii方式輸出到顯示屏幕。比如說:int 8,在內存中就是00 00 00 08,而最終輸出到屏幕是一個8(如果用%d輸出),如果用16進制輸出,則屏幕上是00 00 00 08,[2]即會把內存內容用有限的ascii碼顯示出來。10進制就是數字0-9,16進制就是0-9A-B。[3](這裏的輸出順序並不一定就是內存內容的順序,需要考慮大小端的問題。)

所以,最直觀看內存內容的方式,是以16進制和相應的sizeof(type)大小輸出看到底是什麼。比如,如果要看一個int的內存內容是什麼,那麼使用%04x輸出。因爲2進制和16進制轉換容易且直觀,即8位的2進制可以轉爲2位的16進制,2位的16進制就是一個byte。

相比而言,其他進制輸出就不是很容易看出2進制的內存內容

大小端解釋

什麼是大小端?比如8這個int類型,可以看成是00 00 00 08,高31位是0,低1位是8。一個int使用4個bytes,我們分別看每個字節會發現,int的第一個byte的位置上的內存內容是8,第二個到第四個是0,也就是低地址的內存放的是8,高地址內存放的是0。這樣的存放模式就是小端模式。數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中

int t =37;
char * pa = (char*)(&t);
printf("[%d] [%d] [%d] [%d]\n", pa[0], pa[1], pa[2], pa[3]);
printf("[%08x] [%08x] [%08x] [%08x]\n", pa, pa+1, pa+2, pa+3);

// result
// [37] [0] [0] [0]
// [cc354bdc] [cc354bdd] [cc354bde] [00000000cc354bdf]


int t1 = 2621495; //對應的16進制是28 00 37
pa = (char*)(&t1);
printf("[%d] [%d] [%d] [%d]\n", pa[0], pa[1], pa[2], pa[3]);

// result
// [55] [0] [40] [0](10進制輸出),轉爲16進製爲[37] [00] [28] [00]

long t2 = 56076300985344; //對應的16進制是33 00 48 00 24 00
pa = (char*)(&t2);
printf("[%d] [%d] [%d] [%d] [%d] [%d] [%d] [%d]\n", pa[0], pa[1], pa[2], pa[3], pa[4], pa[5], pa[6], pa[7]);

// result
// [0] [36] [0] [72] [0] [51] [0] [0] 轉爲16進製爲[00] [24] [00] [48] [00] [33] [00] [00]

 

現在要考慮char如何轉爲int和int如何轉爲char,進一步地,就是單字節和多字節之間的轉換。首先介紹一下snprintf這個函數。snprintf是sprintf的邊界檢查版本,具體可以百度


char b[9];
snprintf(b, sizeof(b), "%d", 0x7FFFFFFF); //對應10進制2147483647
int i = 0;
for(i = 0; i< 9; i++)
    printf("%d [%c]  ", i, b[i]);

printf("\n");

for(i = 0; i< 9; i++)
    printf("%d [%02x]  ", i, b[i]);

printf("\n");

// result
// 0 [2]  1 [1]  2 [4]  3 [7]  4 [4]  5 [8]  6 [3]  7 [6]  8 []
// 0 [32]  1 [31]  2 [34]  3 [37]  4 [34]  5 [38]  6 [33]  7 [36]  8 [00]

這裏涉及到上文的[2],以及[1]。這裏將int轉成ascii保存進內存中。事實上這個snprintf函數會將int的每一位升格,保存到一位內存中,並且以ascii方式保存。

當有一片內存,如果想取某幾位做與或非,或者想查看某幾個內存內容,那麼根據需求,可以取2個bytes short,4個bytes int,8個bytes long。但是要注意一下大小端。

int s = *(int*)b;
printf("%d, %04x", s, s);

// result
// 926167346, 37343132

內存位置內容,從左到右,內存地址由低到高

32 31 34 37

對應的int,小端模式,16進制,對應10進製爲926167346

37343132

如果需要取int s的低8位,指的是內存地址的低8位

b & 0x000000FF

因爲數據位低代表內存位低

其實說了半天,就是當使用不同的數據類型,解讀內存的方式不同!而難點在於使用什麼規則取解讀,,,,

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章