深入理解计算机系统-之-数值存储(二)--C程序打印变量的每一字节或者位

大端与小端



前面我们提到了依据CPU端模式的不同,数据的存储顺序也不一样。

采用大小模式对数据进行存放的主要区别在于在存放的字节顺序,BE big-endian 大端模式 ,最直观的字节序 ,地址低位存储值的高位,地址高位存储值的低位 ,不需要考虑对应关系,只需要把内存地址从左到右按照由低到高的顺序写出 ,把值按照通常的高位到低位的顺序写出 ,两者对照,一个字节一个字节的填充进去

LE little-endian 小端模式,最符合人的思维的字节序,地址低位存储值的低位,地址高位存储值的高位 ,怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说,低位值小,就应该放在内存地址小的地方,也即内存地址低位 反之,高位值就应该放在内存地址大的地方,也即内存地址高位。

具体参照深入理解计算机系统-之-数值存储(一)-CPU大端和小端模式详解

打印变量的的每一个字节


算法分析



但是理论我们已经讲的很详细了,却没有真正看过数据的存储结果,因此我们期待能够利用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;
}

转载:http://blog.csdn.net/gatieme/article/details/50687118

时间: 2024-08-01 10:10:17

深入理解计算机系统-之-数值存储(二)--C程序打印变量的每一字节或者位的相关文章

深入理解计算机系统-之-数值存储(六)--以不同的方式窥视内存

浮点数写,整数读 好了知道了浮点数的存储方式,那么我们的问题来了,如果我们定义了一个浮点数,那么如果以整数的格式去读取它,会发生什么奇妙的现象 代码示例 我们对上一篇文章中修改main函数为如下形式 我们定义了变量float f = 9.0f, 然后用一个整形指针去读它,由于32位机器上int和float类型的大小是一致的,不会存储访问越界(即使是64位机器,int也不一定为不是32位,因为这个不仅跟机器字长相关,还跟编译器的处理方式相关) #include <stdio.h> #includ

深入理解计算机系统-之-数值存储(一)-CPU大端和小端模式详解

大端与小端 在嵌入式开发中,大端(Big-endian)和小端(Little-endian)是一个很重要的概念. MSB与LSB 最高有效位(MSB)指二进制中最高值的比特.在16比特的数字音频中,其第1个比特便对16bit的字的数值有最大的影响.例如,在十进制的15,389这一数字中,相当于万数那1行(1)的数字便对数值的影响最大.比较与之相反的"最低有效位"(LSB). LSB(Least Significant Bit),意为最低有效位:MSB(Most Significant

深入理解计算机系统-之-数值存储(三)-- 原码、反码、补码和移码详解

原码 如果机器字长为n,那么一个数的原码就是用一个n位的二进制数,其中最高位为符号位:正数为0,负数为1.剩下的n-1位表示概数的绝对值. PS:正数的原.反.补码都一样:0的原码跟反码都有两个,因为这里0被分为+0和-0. 原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制: [+1]原 = 0000 0001 [-1]原 = 1000 0001 例如: X=+101011 , [X]原= 00101011 X=-101011 , [X]原= 1010

深入理解计算机系统之旅(二)信息在计算机中的表示和处理

1.前言 计算机只能处理和认识0和1,但是0和1的位数多了,就能对现实世界的各种信息进行编码,同样的0和1的序列在不同场景和上下文中的意义也不尽相同,所以通过一定的规则进行合理的编码和解码,然后就能将现实世界的信息放在计算机中运算和处理,最后按照预先设定好的程序将结果反馈给用户. 2.信息的存储 3.整数的表示 4.整数的运算 5.浮点数(小数) 6.小结 未完待续

深入理解计算机系统之旅(三)程序的机器级表示

1.前言 我们在使用高级语言比如C语言.OBjective-C以及Java等编写程序时,我们被屏蔽了程序的具体机器级的实现,这个屏蔽的工具就是编译器(Java是解释执行,但是它同样也需要将文本的源码进行一定的翻译,转换为字节码). 未完待续

深入理解计算机系统-之-内存寻址(五)--页式存储管理

前景回顾 前面我们讲解了操作系统段式存储管理的主要内容. 32位,在保护方式下,其能够访问的线性地址空间可达4GB,而且允许几乎不受存储空间限制的虚拟存储器程序.虚拟存储器地址空间能够可达64TB.它还提供了复杂的存储管理和硬件辅助的保护机构和增加了支持多任务操作系统的特别优化的指令.实际上,64TB的虚拟地址空间是有磁盘等外部存储器的支持下实现的.在编写程序是可以放在磁盘存储器上,但在执行时,必须把程序加载到物理存储器中.而存储器管理就是要将46位虚拟地址变换成32位物理地址. 将程序分成不同

risc-关于《深入理解计算机系统》中 CISC 和 早期的RISC 的区别

问题描述 关于<深入理解计算机系统>中 CISC 和 早期的RISC 的区别 <深入理解计算机系统>(中文第2版)中236页对比 CISC 和 早期的RISC 的区别中有这样一段:CISC:对机器级程序来说实现细节是不可见的.ISA 提供了程序和如何执行程序之间的清晰的抽象.早期的RISC:对机器级程序来说实现细节是可见的.有些 RISC 机器禁止某些特殊的指令序列,而有些跳转要到下一条指令执行完了以后才会生效.编译器必须在这些约束条件下进行性能优化. 解决方案 这个真的很有难度,

c语言-深入理解计算机系统 8.3题

问题描述 深入理解计算机系统 8.3题 #include int main() { if(fork()==0){ printf("a"); } else{ printf("b"); waitpid(-1,NULL,0); } printf("c"); exit(0); 求问这个程序的可能输出序列? 答案给出了acbc,bcac,abcc,bacc四种答案. 但是我觉得不应该有四种,因为fork以后有两个进程,但是父进程会遇到waitpid,这个必

linux基础-深入理解计算机系统疑问

问题描述 深入理解计算机系统疑问 为什么说代码段总是从0x08048000开始,这句话说的是所有程序的代码段吗? 解决方案 对于所有进程来说,代码都是从同一固定地址开始,如Linux系统通常从0x08048000开始代码段(如前所述,从地址0到代码段起始地址的部分通常为操作系统保留区).代码及只读数据区是直接按照可执行目标文件的内容初始化的,与目标文件中的代码段(.text).初始化段(.init)及只读数据段(.rodata)相对应. 解决方案二: <深入理解计算机系统>(二)深入理解计算机