前言:
此时各位老铁们对“c语言汉字几个字节”大概比较重视,姐妹们都想要学习一些“c语言汉字几个字节”的相关文章。那么小编也在网络上汇集了一些对于“c语言汉字几个字节””的相关知识,希望你们能喜欢,朋友们一起来了解一下吧!构成电子计算机基本逻辑单元的晶体管可以表示两种状态,用二进制描述就是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 //0x0000011110 //0x1e00110000 //0x3000110000 //0x3000011100 //0x1c00110000 //0x3000110000 //0x3000011110 //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;}/*1102631-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 bitsMonth(1-12)4 bitsDay(l-31) 5 bits如2021/11/22 1234567890123456 00000000000000000000000000010101 // 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],126E978Dh0040104B 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-
标签: #c语言汉字几个字节