编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

C|按位、字节级、字长级数据处理(c++字节数)

gowuye 2024-04-25 04:42 7 浏览 0 评论

构成电子计算机基本逻辑单元的晶体管可以表示两种状态,用二进制描述就是0或1,称为一个二进制位(bit),多个晶体管的组合可以实现逻辑电路,数据和指令都可以以二进制的序列来表示。

通常以8个二进制位组成一个字节(byte),以字节为单位进行编址。

CPU在单位时间内能一次处理的二进制数的位数叫字长(word size)。字长指明指针数据的标称大小(nominal size)。对于一个字长为n位的机器而言,虚拟地址的范围为0-2^n-1,程序最多可以访问2^n个字节。如32位机器的虚拟地址范围为0x~0xFFFFFFFF。
32位CPU表示该CPU处理的字长为32位,即一次处理4个字节。32位操作系统表示支持32位的CPU。
64位CPU —指的是该CPU处理的字长为64位,即一次处理8个字节。64位操作系统表示支持64位的CPU,操作系统通常向后兼容,所以也支持32位操作系统。

使用PAE36技术的32位CPU是36根地址线,使用PAE40技术的Intel x86-64 CPU是40根地址线,使用PAE52技术的AMD x86-64 CPU是52根地址线。

C语言支持位级(bitwise)操作,其数据类型类型有一个字节长度的char类型,一个字长的int类型,指针本身的存储也使用一个字长。

1 位级(bitwise)处理

C语言支持按位与、或、非操作,也支持移位操作。位操作的一个显著用途就是节省存储空间。

如在公历转农历的程序中,可以使用一个unsigned int来存储一个年份中的诸多信息:

如2019年的信息用0x0A9345表示,其二进制位为0000 1010 1001 00110 10 00101。

(16进制解析,每一个16进制位(0-f)是4个二进制位:0000-1111)

20-23位,其十进制值表示闰月月份,值为0 表示无闰月(第23位代表最高位,最左边)

7到19位,分别代表农历每月(在闰年有13个月)的大小,每一位代表一个月份。

(1表示大月为30天,0表示小月29天)

5到6位,其十进制值表示春节所在公历月份(此处是2月)

0到4位,其十进制值表示春节所在公历日期(此处是5日)

解析出不同的位组便可以得到该年不同的信息。

不同的年份可以存储到一个数组中:

unsigned int LunarCalendarTable[199] =
{
0x069349,0x7729BD,0x06AA51,0x0AD546,0x54DABA,0x04B64E,0x0A5743,0x452738,0x0D264A,0x8E933E,/*2081-2090*/
0x0D5252,0x0DAA47,0x66B53B,0x056D4F,0x04AE45,0x4A4EB9,0x0A4D4C,0x0D1541,0x2D92B5 /*2091-2099*/
};	

点阵数字和字符信息也可以用字符数组存储和处理:

/*
输出点阵数字:8个char即可保存64个位的数据,例如3:
a[3]({0x00,0x1e,0x30,0x30,0x1c,0x30,0x30,0x1e}, //3
包括有8个十六进制的数,每行一个十六进制数,并且换成二进制的表示,会是什么样的呢?

00000000  //0x00
00011110  //0x1e
00110000  //0x30
00110000  //0x30
00011100  //0x1c
00110000  //0x30
00110000  //0x30
00011110  //0x1e
请看1出现的地方,可以借着鼠标按1出现的轨迹跟着划一划,不就是 数字3字型的轮廓吗?
只不过,耳朵状的3是反着的(这自有道理,看完程序1自会明白)。
————————————————
*/
#include <iostream>
using namespace std;
char a[10][8]=
{
    {0x00,0x18,0x24,0x24,0x24,0x24,0x24,0x18}, //0
    {0x00,0x18,0x1c,0x18,0x18,0x18,0x18,0x18}, //1
    {0x00,0x1e,0x30,0x30,0x1c,0x06,0x06,0x3e}, //2
    {0x00,0x1e,0x30,0x30,0x1c,0x30,0x30,0x1e}, //3
    {0x00,0x30,0x38,0x34,0x32,0x3e,0x30,0x30}, //4
    {0x00,0x1e,0x02,0x1e,0x30,0x30,0x30,0x1e}, //5
    {0x00,0x1c,0x06,0x1e,0x36,0x36,0x36,0x1c}, //6
    {0x00,0x3f,0x30,0x18,0x18,0x0c,0x0c,0x0c}, //7
    {0x00,0x1c,0x36,0x36,0x1c,0x36,0x36,0x1c}, //8
    {0x00,0x1c,0x36,0x36,0x36,0x3c,0x30,0x1c}, //9
};
int main()
{
    int n=0,i,j,k,m,x;
    cout<<"请输入需要显示的数字:";
    int c[8];
    cin>>n;
    for(k=0; n&&k<8; k++)   //c数组将分离出n中的各位数,不过是倒着的,例n=123,c中保存3 2 1
    {
        c[k]=n%10;
        n/=10;
    }  //循环结束,将由k记住n是几位数,此处限最多8位数
    
    for(i=0; i<8; i++)   //一共要显示8行,不是依次显示k个数字,而是依次显示k个数字中对应的每一行
    {
        for(m=k-1; m>=0; m--)  //要显示n=123, c中是倒着保存各位数的,所以m由大到小
        {
 
            x=a[c[m]][i];     //现在要显示的数字是c[m],所以取a数组中的第c[m]行,第i列数据
            for(j=0; j<8; j++)
            {
                if(x%2)
                    cout<<'*';
                else
                    cout<<' ';
                x=x/2;
            }
        }
        cout<<endl;
    }
    while(1);
    return 0;
}
/*
请输入需要显示的数字:68

  ***     ***
 **      ** **
 ****    ** **
 ** **    ***
 ** **   ** **
 ** **   ** **
  ***     ***
*/

浮点编码可以通过位域来解析:

#include <stdio.h>

void floatNumber_1(){
    struct FF{          // 小端模式模拟double类型编码
        unsigned l:32;  // 剩下的小数位
        unsigned m:15;  // 剩下的小数位
        unsigned k:5;   // 取5位小数
        unsigned j:11;  // 阶码
        unsigned i:1;   // 符号位
    };    
    union UN
    {
        double dd;
        FF ff;
    };
    UN un;
    un.dd = -15.75;         //  -1111.11
    
    printf("%d\n",un.ff.i); // 1
    printf("%d\n",un.ff.j); // 1023+3
    printf("%d\n",un.ff.k); // 31 也就是二进制的11111
}
void floatNumber_2(){
    struct FF{          // 小端模式模拟double类型编码
        unsigned l:32;  // 剩下的小数位
        unsigned m:15;  // 剩下的小数位
        unsigned k:5;   // 取5位小数
        unsigned j:11;  // 阶码
        unsigned i:1;   // 符号位
    };   
    union UN
    {
        double dd;
        FF ff;
    };
    UN un;
    
    un.ff.i = 1;
    un.ff.j = 1023+3;
    un.ff.k = 31; // 二进制的11111
    un.ff.m = 0;
    un.ff.l = 0;
    printf("%.2lf\n",un.dd); //un.dd = -15.75;//  -1111.11
}

int main()
{
    floatNumber_1();
    floatNumber_2();
    while(1);
    return 0;
}
/*
1
1026
31
-15.75
*/

位域、共用体和可以解析汉字的GBK或GB2312编码:

void cngb()
{
    union{
        struct {
            unsigned int i:4;
            unsigned int j:4;
            unsigned int k:4;
            unsigned int L:4;
            unsigned int m:4;
            unsigned int n:4;
        };
        char hanzi[3];
    }hz;
    fflush(stdin);
    puts("查询gb2312码,请输入一个汉字:");
    gets(hz.hanzi);
    //strcpy(hz.hanzi,"中");
    printf("%X%X%X%X\n",hz.j,hz.i,hz.L,hz.k);
}

2 字节级(byte)处理

典型类型:char,char的长度是一个字节。

<limits.h>定义了一个宏:CHAR_BIT。

typedef unsigned char byte;

显示double的字节编码:

void showBytes(unsigned char* start, int len)
{
    for(int i=0;i<len;i++)
        printf(" %.2x",start[i]);
    printf("\n");
}

void showDoubleByte(double x)
{
    showBytes((unsigned char*)&x,sizeof(double));
}

void showBytesByBig(unsigned char* start, int len)
{
    unsigned int i = 0x00000001;
    unsigned char* c = (unsigned char*)&i;
    if(*c==1)// 小端返回true,大端返回0
    {
        for(int i=len-1;i>=0;i--)
            printf(" %.2x",start[i]);
        printf("\n");
    }
}

memmove()函数实现:

memmove()由src所指定的内存区域赋值count个字符到dst所指定的内存区域。 src和dst所指内存区域可以重叠,但复制后src的内容会被更改。函数返回指向dst的指针。

void * my_memmove(void * dst,const void * src,int count)
{
    void * ret = dst;
    if(dst <= src || (char *)dst >= ((char *)src + count))
    {
        while(count--)
        {
            *(char *)dst = *(char *)src;
            dst = (char *)dst + 1;
            src = (char *)src + 1;
        }
    }
    else
    {
        dst = (char *)dst + count - 1;
        src = (char *)src + count - 1;
        while(count--)
        {
            *(char *)dst = *(char *)src;
            dst = (char *)dst - 1;
            src = (char *)src - 1;
        }
    }
    return(ret);
}
int main()
{
    char a[12];
    puts((char *)my_memmove(a,"ammana_babi",16));
    system("pause");
    return 0;
}

二进制文件浏览:

#include<iostream>  
#include<iomanip>
#include <fstream>
#include<cstdlib>
using namespace std;
 
int main( )
{
    char c[16];
    char f[100];
    cout<<"请输入文件名:";
    cin>>f;
    ifstream infile(f,ios::in|ios::binary);
    if(!infile)
    {
        cerr<<"open error!";
        exit(1);
    }
    while(!infile.eof())
    {
        infile.read(c,16);
        if(!infile.eof())
        {
            for(int i=0; i<16; ++i)
                cout<<setfill('0')<<setw(2)<<hex<<int((unsigned char)(c[i]))<<" ";
            cout<<'\t';
            for(int i=0; i<16; ++i)
                cout<<(c[i]?c[i]:'.');
            cout<<endl;
        }
    }
    return 0;
}

位级与字节级结合处理的实例:

考虑用一个short类型存储一个有效日期(年份取末两位):

/* 
考虑用一个short类型存储一个有效日期(年份取末两位):
Year(0-99) 7 bits
Month(1-12)4 bits
Day(l-31)  5 bits
如2021/11/22
         1234567890123456
         0000000000000000
0000000000010101           // year左移9位留下7位有效位
    0000000000001011       // Month左移5位留给Day
         0000000000010110
*/
// 向整数中压缩数据 
#include <iostream>
#include <iomanip>
using namespace std;
unsigned short dateShort(short year,short mon,short day)
{
    unsigned short date;
    date = (year << 16-7) | (mon << 5) | day; 
    return date;
}
void datePrint(unsigned short date)
{
    struct Date{
        unsigned day:5;
        unsigned mon:4;
        unsigned year:7;
    };
    Date *d = (Date*)&date;
    printf("20%d/%d/%d",d->year,d->mon,d->day);
}
int main()
{
    unsigned short date = dateShort(21,11,22);
    datePrint(date); // 2021/12/22
    getchar();
    return 0;
}

按16进制显示数据:

#include <stdio.h>

void hexPrint(int n)
{
    if(n==0)
        printf("00 ");
    char str[5] = {0};
    int len = 0;
    while(n)
    {
        int m = n%16;
        n /= 16;
        if(m<10)
            str[len++] = m + '0';
        else
            str[len++] = m + 'A'-10;
    }
    --len;
    if(len%2==0)
        printf("0");
    while(len>=0)
    {
        printf("%c",str[len--]);
        if(len%2)
            printf(" ");
    }
}

hexPrint2(int n)
{
    char str[5] = {0};
    sprintf(str,"%X",n);
    printf("%s ",str);
}

void bitsPrint(void *type,unsigned size)
{
    unsigned char*p = (unsigned char*)type;
    int endian = 1;
    if(*(char*)&endian)
        printf("小端字节序:");
    for(unsigned i=0;i<size;i++)
        //printf("%d ",*p++);
        hexPrint(*p++);
        //hexPrint2(*p++);
    printf("\n");
}

int main()
{
    int a = -123456789;
    bitsPrint((void*)&a,sizeof a); // 小端字节序:EB 32 A4 F8
    double b = -15.75;  //
    bitsPrint((void*)&b,sizeof b); // 小端字节序:00 00 00 00 00 80 2F C0
    getchar();
    return 0;
}

3 字级(word)处理

典型类型:int,int的长度是一个字长,32位CPU或操作系统是4个字节,64位是8个字节。

typedef unsigned int word;

3.1 寄存器的长度是一个字长

当读写double数据类型时,需要两条mov指令:

10:       double dd = 15.751;
00401044   mov         dword ptr [ebp-18h],126E978Dh
0040104B   mov         dword ptr [ebp-14h],402F8083h

当读写一个字长或以下的数据时,只需要一个寄存器,一条mov指令。

同样的,当返回值是一个字长或以下的数据时,可用寄存器返回。如果是double,则用浮点栈返回,如果是复合类型,则需要压入一个存储返回值的起始地址,将返回值返回到这个起始地址标识的被调函数的栈帧空间。

3.2 指针的标度是一个字长

printf("%d\n",sizeof(void*)); // 4,32位系统

3.3 栈按一个字长对齐

其根源还是在于寄存器的长度是一个字长,一次访问一个字长的内存空间,如果不对齐,有可能就需要更多次的访问,适当的浪费一点内存空间来换取效率(以空间换时间)是可取的。

#include <stdio.h>

void bufferOverflow()
{
    char ch = 'a';   // 栈对齐为4个字节
    int base = 0;
    char buf[5] = {0}; // 栈对齐为8个字节
    puts("输入你构造的字符串,模拟缓冲区溢出:");
    gets(buf);
    if(base==0x64636261){
        puts("缓冲区溢出改写了邻近区内存!");
    }
}

int main()
{
    bufferOverflow(); // 输出12345678abcd会执行puts(),678用于栈对齐,
                                 // abcd给到了base,'\0'给到了ch
    return 0;
}
/*output:
输入你构造的字符串,模拟缓冲区溢出:
12345678abcd
缓冲区溢出改写了邻近区内存!
*/

看函数的栈帧:

栈帧图示:

如果输入超过15个字符(其中有'\0'),则会破坏ebp,引发运行错误。

结构体也需要同样的对齐(包括成员的对齐及整体的对齐):

#include <stdio.h>
struct Align{
    char ch;
    int base;
    char buf[5];
};
int main()
{
    Align align = {'a',1,"abcd"};
    getchar();
    return 0;
}

函数内存映像:

-End-

相关推荐

Nginx 响应提速10倍,你需要知道的缓存性能优化——FastCGI调优
Nginx 响应提速10倍,你需要知道的缓存性能优化——FastCGI调优

Nginx缓存优化是帮助大家提升网站性能的重要操作之一,proxy_cache主要用于反向代理时,对后端内容源服务器进行缓存;fastcgi_cache主要用于...

2024-05-20 14:44 gowuye

王者荣耀天魔缭乱和逐梦之音返场活动地址 3月22日开启返场活动
王者荣耀天魔缭乱和逐梦之音返场活动地址 3月22日开启返场活动

王者荣耀官方终于确定了天魔缭乱和逐梦之音的返场活动,这让不少小伙伴乐开了花,返场活动将会在3月22日开启,下面就带来王者荣耀天魔缭乱和逐梦之音返场活动地址!王者...

2024-05-20 14:44 gowuye

常见的嵌入式web服务器有哪些?

嵌入式WEB服务器常见的有:Lighttpd,Shttpd,Thttpd,Boa,Mini_httpd,Appweb,Goahead。Lighttpd地址:http://www.light...

简述几款常见的嵌入式web服务器
简述几款常见的嵌入式web服务器

嵌入式web服务器,是web服务器当中的一种,是基于嵌入式系统而实现的web服务器。指的是在嵌入式系统(通俗点就是单片机系统)上实现的一个web服务器,可以通过...

2024-05-20 14:44 gowuye

教你如何利用fastcgi_cache缓存加速WordPress

在使用nginx缓存之前,必须在nginx里面加载专门的模块,这个模块叫做ngx_cache_purge。添加ngx_cache_purge模块下载ngx_cache_purge模块ngx_cache...

扫描WordPress漏洞

检测已知漏洞WPScan是一款广泛使用的WordPress安全扫描工具,它的一项重要功能是检测已知漏洞。在这篇文章中,我们将深入探讨WPScan如何检测已知漏洞,并结合实际示例,帮助读者更好地理解和应...

消灭 Bug!推荐几个给力的开源 Bug 跟踪工具
消灭 Bug!推荐几个给力的开源 Bug 跟踪工具

在这个充满bug的世界里,最遥远的距离不是生与死,而是你亲手制造的bug就在你眼前,你却怎么都找不到它。因此本文准备了7款优秀的开源bug跟踪系...

2024-05-20 14:43 gowuye

生物信息分析入门全攻略

生物信息学是生命科学研究的重大前沿领域,未来将占据生命科学研究的半壁江山。已经有越来越多的小伙伴投入到生物信息的学习中,但是入门难、深入慢、摸不到方向等都成为持续学习的拦路虎。本文根据生物信息技术大牛...

elkb实践经验,再赠送一套复杂的配置文件
elkb实践经验,再赠送一套复杂的配置文件

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。宝剑锋从磨砺出,梅花香自苦寒来。诗人白居易,三月下江南,看到沿路开放的桃花,心潮澎湃...

2024-05-20 14:43 gowuye

超详细从0到1 搭建ELK监控
超详细从0到1 搭建ELK监控

监控分类?Metrics用于记录可聚合的数据。例如,1、队列的当前深度可被定义为一个度量值,在元素入队或出队时被更新;HTTP请求个数可被定义为一个计数器,...

2024-05-20 14:42 gowuye

嵌入式开发 之Web配置页面开发
嵌入式开发 之Web配置页面开发

1.PHP是最好的语言??开发动态页面首选的语言是PHP,村村不能在这里忽悠人,如果你的硬件性能允许切略懂PHP,看到这里就可以退出了。本文面向的受众是Linu...

2024-05-20 14:42 gowuye

Python开发一个网站目录扫描工具用来检测网站是否有漏洞?
Python开发一个网站目录扫描工具用来检测网站是否有漏洞?

开发一个网站目录扫描工具是用来检测网站是否有非法目录请求的一个常见需求之一,我们要通过这个扫描工具来找到通过某个域名可以访问到的网站路径,可能对于有些系统来讲,...

2024-05-20 14:42 gowuye

创建一个类似Youtube的Id——使用PHP/Python/JS/Java/SQL

id通常都是用数字,不巧的是只有10个数字来使用,所以如果你有很多的记录,id往往变得非常冗长。当然对于计算机来说无所谓,但我们更希望id尽可能短。所以我们如何能使id变短?我们可以利用字母让它们附加...

快速云:有助于移动应用安全开发的五条妙计
快速云:有助于移动应用安全开发的五条妙计

许多企业不断地向其开发团队提供培训。但是某些漏洞,如早在十多年前就发现的SQL注入,如今仍广泛存在于各种应用中。因而,安全培训永不过时。在开发移动应用时,开发者...

2024-05-20 14:41 gowuye

洛杉矶国际电影节最佳动画短片奖影片《G’DAY》正式全网上映
洛杉矶国际电影节最佳动画短片奖影片《G’DAY》正式全网上映

7月2日,由M&CSaatchi创作,由深受好评的澳大利亚导演迈克尔·格雷西执导的动画短片《G’day》,正式在全网上映。该影片因其出色的创意赢得了洛...

2024-05-20 14:41 gowuye

取消回复欢迎 发表评论: