1. 程式人生 > >深入理解計算機系統-之-數值儲存(二)--C程式列印變數的每一位元組或者位

深入理解計算機系統-之-數值儲存(二)--C程式列印變數的每一位元組或者位

大端與小端

前面我們提到了依據CPU端模式的不同,資料的儲存順序也不一樣。

採用大小模式對資料進行存放的主要區別在於在存放的位元組順序,BE big-endian 大端模式 ,最直觀的位元組序 ,地址低位儲存值的高位,地址高位儲存值的低位 ,不需要考慮對應關係,只需要把記憶體地址從左到右按照由低到高的順序寫出 ,把值按照通常的高位到低位的順序寫出 ,兩者對照,一個位元組一個位元組的填充進去

LE little-endian 小端模式,最符合人的思維的位元組序,地址低位儲存值的低位,地址高位儲存值的高位 ,怎麼講是最符合人的思維的位元組序,是因為從人的第一觀感來說,低位值小,就應該放在記憶體地址小的地方,也即記憶體地址低位 反之,高位值就應該放在記憶體地址大的地方,也即記憶體地址高位。

列印變數的的每一個位元組

演算法分析

但是理論我們已經講的很詳細了,卻沒有真正看過資料的儲存結果,因此我們期待能夠利用C語言編寫程式輸出變數的的每一位

思路:

C語言中char 必須對應一個byte , 所以它的型別固定是1個位元組。

用一個char*的指標指向變數的首地址,往後順序讀取sizeof個位元組的資料,就可以訪問到變數的每一位

/*
addr  -=> 待列印的變數的首地址 
size  -=>·待列印的變數的大小 
return 成功返回列印的位元組數  
*/
int print_all_byte(void *addr, int
size) { unsigned char *ptr = (unsigned char *)addr; int print_bytes = 0; if(ptr == NULL) { return -1; } while(print_bytes < size) { printf("%02x", *ptr); ptr++; print_bytes++; } printf("\n"); return print_bytes; }

示例程式

首先我們判斷一下當前電腦的大小端模式,然後分別定義了short,int,long,float,double,array陣列幾種型別的資料。
然後分別列印了它的每一個位元組的資訊。

#include <stdio.h>
#include <stdlib.h>


int check_end()
{
    int   i = 0x12345678;
    char *c = (char *)&i; 

    return (*c == 0x12);
}


int CheckEnd()
{
    union
    {
        int a;
        char b;
    }u;

    u.a = 1;
    if (u.b == 1)
        return 0;
    else 
        return 1;
}

/*
addr  -=> 待列印的變數的首地址 
size  -=>·待列印的變數的大小 
return 成功返回列印的位元組數  
*/
int print_all_byte(void *addr, int size)
{
    unsigned char *ptr = (unsigned char *)addr;
    int print_bytes = 0;

    if(ptr == NULL)
    {
        return -1; 
    }

    while(print_bytes < size)
    {
        printf("%02x", *ptr); 
        ptr++; 
        print_bytes++; 
    }
    printf("\n"); 
    return print_bytes; 
}

int main(void)
{
    if(check_end() == 1)
    {
        printf("大端\n");
    }
    else
    {
        printf("小端\n");
    }

    short shortvalue = 0x1234; 
    if(print_all_byte((void *)&shortvalue, sizeof(shortvalue)) != -1)
    {
        printf("print SHORT success!\n\n"); 
    }

    int intvalue = 0x12345678; 
    if(print_all_byte((void *)&intvalue, sizeof(intvalue)) != -1)
    {
        printf("print INT success!\n\n"); 
    }

    long longvalue = 0x87654321; 
    if(print_all_byte((void *)&longvalue, sizeof(longvalue)) != -1)
    {
        printf("print LONG success!\n\n"); 
    }

    float floatvalue = 0.12345678; 
    if(print_all_byte((void *)&floatvalue, sizeof(floatvalue)) != -1)
    {
        printf("printf FLOAT success!\n\n"); 
    }

    double doublevalue = 0.12345678; 
    if(print_all_byte((void *)&doublevalue, sizeof(doublevalue)) != -1)
    {
        printf("printf DOUBLE success!\n\n"); 
    }

    int array[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 1234}; 
    if(print_all_byte((void *)array, sizeof(array)) != -1)
    {
        printf("printf ARRAY success!\n\n"); 
    }


    return EXIT_SUCCESS; 
}

這裡寫圖片描述

列印變數的的每一個位

演算法分析

前面通過char我們可以讀取到變數的每個位元組,我們進一步拓展,讀取每一個位元組後,再取出其對應的每一位,即可按照二進位制的方式輸出每個位。
讀取每一位的操作,即判斷某一位是1還是0,可以採用位運算完成,具體操作如下。

int isset(char data, int bit)
{ 
    data >>= bit;

    if(data & 1 == 0) 
    {
        return 0; 
    }
    else
    {
        return 1; 
    }
}

所以我們對上面的演算法進行拓展,先取到每一個byte,然後再讀取該byte的每一個bit。

int print_bit(char *addr, int size)
{

    unsigned char *ptr = (unsigned char *)addr;
    int print_bytes = 0;

    if(ptr == NULL)
    {
        return -1; 
    }

    for(print_bytes = 0;
        print_bytes < size;
        print_bytes++, ptr++)
    {
        for(int print_bits = 7;
        print_bits >= 0;
        print_bits--)
        {
            printf("%d", ((*ptr >> print_bits) & 1));
        }

    }
    printf("\n"); 
    return print_bytes;
}

示例程式

#include <stdio.h>
#include <stdlib.h>


//#define DEBUG
int isset(char data, int bit)
{ 
    data >>= bit;

    if(data & 1 == 0) 
    {
        return 0; 
    }
    else
    {
        return 1; 
    }
}

/*
addr  -=> 待列印的變數的首地址 
size  -=>·待列印的變數的大小 
return 成功返回列印的位元組數  
*/
int print_bit(char *addr, int size)
{

    unsigned char *ptr = (unsigned char *)addr;
    int print_bytes = 0;

    if(ptr == NULL)
    {
        return -1; 
    }

    for(print_bytes = 0;
        print_bytes < size;
        print_bytes++, ptr++)
    {
#ifdef DEBUG
        printf("byte %d, data = %02x -=>", print_bytes, *ptr); 
#endif
        for(int print_bits = 7;
        print_bits >= 0;
        print_bits--)
        {
            printf("%d", ((*ptr >> print_bits) & 1));
        }
#ifdef DEBUG
        printf("\n");
#endif

    }
    printf("\n"); 
}

int main(void)
{
    /*short shortvalue = 0x1234; 
    if(print_bit((char *)&shortvalue, sizeof(shortvalue)) != -1)
    {
        printf("print SHORT success!\n\n"); 
    }*/

    int intvalue = 0x12345678;
    if(print_bit((char *)&intvalue, sizeof(intvalue)) != -1)
    {
        printf("print INT success!\n\n"); 
    }

    long longvalue = 0x87654321; 
    if(print_bit((char *)&longvalue, sizeof(longvalue)) != -1)
    {
        printf("print LONG success!\n\n"); 
    }

    float floatvalue = 0.12345678; 
    if(print_bit((char *)&floatvalue, sizeof(floatvalue)) != -1)
    {
        printf("printf FLOAT success!\n\n"); 
    }

    double doublevalue = 0.12345678; 
    if(print_bit((char *)&doublevalue, sizeof(doublevalue)) != -1)
    {
        printf("printf DOUBLE success!\n\n"); 
    }

    int array[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 1234}; 
    if(print_bit((char *)array, sizeof(array)) != -1)
    {
        printf("printf ARRAY success!\n\n"); 
    }


    return EXIT_SUCCESS; 
}

這裡寫圖片描述

相關推薦

深入理解計算機系統--數值儲存--C程式列印變數位元組或者

大端與小端 前面我們提到了依據CPU端模式的不同,資料的儲存順序也不一樣。 採用大小模式對資料進行存放的主要區別在於在存放的位元組順序,BE big-endian 大端模式 ,最直觀的位元組序 ,地址低位儲存值的高位,地址高位儲存值的低位 ,不需要考慮對

深入理解計算機系統--數值儲存-- 原碼、反碼、補碼和移碼詳解

原碼 如果機器字長為n,那麼一個數的原碼就是用一個n位的二進位制數,其中最高位為符號位:正數為0,負數為1。剩下的n-1位表示概數的絕對值。 PS:正數的原、反、補碼都一樣:0的原碼跟反碼都有兩個,因為這裡0被分為+0和-0。 原碼就是符號位

深入理解計算機系統--數值儲存-CPU大端和小端模式詳解

大端與小端 在嵌入式開發中,大端(Big-endian)和小端(Little-endian)是一個很重要的概念。 MSB與LSB 最高有效位(MSB)指二進位制中最高值的位元。在16位元的數字音訊中,其第1個位元便對16bit的字的數值有最大的

深入理解計算機系統》讀書筆記ch2+ C 泛型

tex byte 指向 get 讀書筆記 class its n) 支持 本章主要介紹各類型的機器表示,Stanford的CS107的lec2和lec3有精彩解釋,比看書快(當作書中只是的cache吧)。 lec4中介紹的C裏面如何使用泛型(沒有template, refe

深入理解計算機系統--記憶體定址--linux中分段機制的實現方式

linux中的分段機制 前面說了那麼多關於分段機制的實現,其實,Linux以非常有限的方式使用分段。因為,Linux基本不使用分段的機制(注:並不是不使用,使用分段方式還是必須的,會簡化程式的編寫和執行方式),或者說,Linux中的分段機制只是為了相容IA

深入理解計算機系統虛擬存儲器

fragment 策略 動態鏈接 字段 索引 ~~ cti 錯誤 個數 http://blog.csdn.net/al_xin/article/details/38590931 進程提供給應用程序的關鍵抽象: 一個獨立的邏輯控制流,它提供一個假象,好像我們的程序獨占地

深入理解計算機系統》——讀書筆記

img 可執行 即將 簡單的 world std 加載 完整 .exe   這本書從一個簡單的C語言的HelloWorld程序講起...   這是這個小程序的生命周期的一個部分:   HellOWorld程序,從被創建(文本格式),到被執行(在屏幕上打印出來)。   其

深入理解計算機系統》學習筆記

一、資訊就是位 + 上下文 作者使用的標題是:資訊就是位 + 上下文,那麼問題來了:什麼是位?什麼是上下文? 計算機系統是由硬體和系統軟體組成的,它們共同工作來執行應用程式。所有計算機系統都有相似的硬體和軟體元件,它們執行著相似的功能。 從某種意義上來說,本書的目的就是要幫助你

深入理解計算機系統 lab1 ——datalab 解答 95個ops

/* * CS:APP Data Lab * * <Please put your name and userid here> * * bits.c - Source file with your solutions to the Lab.

深入理解計算機系統》讀書筆記第一章

主要內容 0.計算機系統是由硬體和系統軟體組成的,它們共同工作來執行應用程式。 1.程式是怎樣編譯執行。 2.程序,執行緒,虛擬記憶體,檔案等基本概念。 3.本書內容提前瀏覽。 資訊就是位+上下文 本書用了一個hello.c的表示方法說明了一個思

深入理解計算機系統整型與浮點型

在計算機儲存系統裡面,算術型別可以分為兩類:整型(intergral type,包括字元和布林型別在內)和浮點型。在看簡單地看了深入理解計算機系統的第二章後,有了稍微深刻但是有非常淺顯的理解,然後又看了阮師兄的一篇博文,所以做了一點筆記。 下面先來看一個例子程

深入理解計算機系統》| 學習筆記

一、資訊就是位 + 上下文 作者使用的標題是:資訊就是位 + 上下文,那麼問題來了:什麼是位?什麼是上下文? 計算機系統是由硬體和系統軟體組成的,它們共同工作來執行應用程式。所有計算機系統都有相似的硬體和軟體元件,它們執行著相似的功能。 從某種意義上來說,本書的目的

深入理解計算機系統》關於csapp.h和csapp.c的編譯問題(轉)

系統 文件中 class net 工作 inux 而且 pan div 編譯步驟如下: 1.我的當前工作目錄為/home/sxh2/clinux,目錄下有3個文件inet_aton.c csapp.h csapp.c。 2.編譯csapp.c文件,命令為gcc -c csa

深入理解javascript原型和閉包14——從【自由變數】到【作用域鏈】

https://www.cnblogs.com/wangfupeng1988/p/3994065.html 重點: var x = 10; function fn(){ console.log(x); } function show(f){ var x = 20; f();

計算機網路IP路由---RIP協議

RIP 基本原理         RIP是基於貝爾曼-福特演算法的一種內部閘道器路徑向量協議。RIP基於UDP封裝路由資訊,在520號埠上偵聽並接收來自遠端路由器傳送的路由更新資訊,並對本地路由器中的路由表做相應的修改在廣播給

深入理解jQuery插件開發總結

如果 要去 init方法 哪裏 eth str 名字空間 extend 重要 1,開始 可以通過為jQuery.fn增加一個新的函數來編寫jQuery插件。屬性的名字就是你的插件的名字: jQuery.fn.myPlugin = function(){

深入理解JS原型與原型鏈

四. proto JS 在建立物件(不論是普通物件還是函式物件)的時候,都有一個叫做__proto__ 的內建屬性,用於指向建立它的建構函式的原型物件。 物件 person1 有一個 __proto__屬性,建立它的建構函式是 Person,建構函式的原型物件是 Person.proto

深入理解java虛擬機器閱讀筆記物件是否存活與垃圾收集演算法

1.1  判斷物件是否存活 1.1.1  引用計數演算法:給每個物件新增一個引用計數器,當一個地方引用此物件時,該計數器值+1;當引用失效時,該計數器值-1;當此物件沒有被引用時,該計數器的值為0。雖然此演算法實現簡單,效率高,但是很難解決兩個物件之間相互迴圈引用的問題。 1.1.2&

ARCore路-計算機視覺機器學習

  機器學習遇到的第一個模型便是線性迴歸,而且是一元線性迴歸,但這個最簡單的模型卻包含了很多的知識點,如矩陣計算、最小二乘、梯度下降等等。這是機器學習的入門,理解這些基本概念對後面的學習有極其重要的影響。在這篇文章中,我們主要對迴歸模型進行闡述,然後還會討論梯度下降演算法。我們力求通俗易

【OO】深入理解ABAP Object Step by Step

第一篇step by step 整理的是如何建立類,並生成類的物件來呼叫類的方法,這一邊著重理解類與類的關係。 1,繼承。 類所具有的特徵: A.類是可以被繼承 B.所有類都是從一個OBJECT的類繼承下來的 C.如果一個類沒有明確表示繼承於某個類,那它就是從OBJECT